code:
# wbuf

#### LICENSE

This software is licensed under the MIT License.

Copyright Fedor Indutny, 2014.

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.


code:
var assert = require('minimalistic-assert');
var Buffer = require('buffer').Buffer;

function WBuf() {
  this.buffers = [];
  this.toReserve = 0;
  this.size = 0;
  this.maxSize = 0;
  this.avail = 0;

  this.last = null;
  this.offset = 0;

  // Used in slicing
  this.sliceQueue = null;

  this.forceReserve = false;

  // Mostly a constant
  this.reserveRate = 64;
}
module.exports = WBuf;

WBuf.prototype.reserve = function reserve(n) {
  this.toReserve += n;

  // Force reservation of extra bytes
  if (this.forceReserve)
    this.toReserve = Math.max(this.toReserve, this.reserveRate);
};

WBuf.prototype._ensure = function _ensure(n) {
  if (this.avail >= n)
    return;

  if (this.toReserve === 0)
    this.toReserve = this.reserveRate;

  this.toReserve = Math.max(n - this.avail, this.toReserve);

  if (this.avail === 0)
    this._next();
};

WBuf.prototype._next = function _next() {
  var buf;
  if (this.sliceQueue === null) {
    // Most common case
    buf = new Buffer(this.toReserve);
  } else {
    // Only for `.slice()` results
    buf = this.sliceQueue.shift();
    if (this.sliceQueue.length === 0)
      this.sliceQueue = null;
  }

  this.toReserve = 0;

  this.buffers.push(buf);
  this.avail = buf.length;
  this.offset = 0;
  this.last = buf;
};

WBuf.prototype._rangeCheck = function _rangeCheck() {
  if (this.maxSize !== 0 && this.size > this.maxSize)
    throw new RangeError('WBuf overflow');
};

WBuf.prototype._move = function _move(n) {
  this.size += n;
  if (this.avail === 0)
    this.last = null;

  this._rangeCheck();
};

WBuf.prototype.slice = function slice(start, end) {
  assert(0 <= start && start <= this.size);
  assert(0 <= end && end <= this.size);

  if (this.last === null)
    this._next();

  var res = new WBuf();

  // Only last chunk is requested
  if (start >= this.size - this.offset) {
    res.buffers.push(this.last);
    res.last = this.last;
    res.offset = start - this.size + this.offset;
    res.maxSize = end - start;
    res.avail = res.maxSize;

    return res;
  }

  var startIndex = -1;
  var startOffset = 0;
  var endIndex = -1;

  // Find buffer indices
  var offset = 0;
  for (var i = 0; i < this.buffers.length; i++) {
    var buf = this.buffers[i];
    var next = offset + buf.length;

    // Found the start
    if (start >= offset && start <= next) {
      startIndex = i;
      startOffset = start - offset;
      if (endIndex !== -1)
        break;
    }
    if (end >= offset && end <= next) {
      endIndex = i;
      if (startIndex !== -1)
        break;
    }

    offset = next;
  }

  res.last = this.buffers[startIndex];
  res.offset = startOffset;
  res.maxSize = end - start;

  // Multi-buffer slice
  if (startIndex < endIndex) {
    res.sliceQueue = this.buffers.slice(startIndex + 1, endIndex + 1);

    res.last = res.last.slice(res.offset);
    res.offset = 0;
  }

  res.avail = res.last.length - res.offset;
  res.buffers.push(res.last);

  return res;
};

WBuf.prototype.skip = function skip(n) {
  if (n === 0)
    return this.slice(this.size, this.size);

  this._ensure(n);

  var left = n;
  while (left > 0) {
    var toSkip = Math.min(left, this.avail);
    left -= toSkip;
    this.size += toSkip;
    if (toSkip === this.avail) {
      if (left !== 0) {
        this._next();
      } else {
        this.avail -= toSkip;
        this.offset += toSkip;
      }
    } else {
      this.offset += toSkip;
      this.avail -= toSkip;
    }
  }

  this._rangeCheck();

  return this.slice(this.size - n, this.size);
};

WBuf.prototype.write = function write(str) {
  var len = 0;
  for (var i = 0; i < str.length; i++) {
    var c = str.charCodeAt(i);
    if (c > 255)
      len += 2;
    else
      len += 1;
  }
  this.reserve(len);
  for (var i = 0; i < str.length; i++) {
    var c = str.charCodeAt(i);
    var hi = c >>> 8;
    var lo = c & 0xff;

    if (hi)
      this.writeUInt8(hi);
    this.writeUInt8(lo);
  }
};

WBuf.prototype.copyFrom = function copyFrom(buf, start, end) {
  var off = start === undefined ? 0 : start;
  var len = end === undefined ? buf.length : end;
  if (off === len)
    return;

  this._ensure(len - off);
  while (off < len) {
    var toCopy = Math.min(len - off, this.avail);
    buf.copy(this.last, this.offset, off, off + toCopy);
    off += toCopy;
    this.size += toCopy;
    if (toCopy === this.avail) {
      if (off !== len) {
        this._next();
      } else {
        this.avail = 0;
        this.offset += toCopy;
      }
    } else {
      this.offset += toCopy;
      this.avail -= toCopy;
    }
  }

  this._rangeCheck();
};

WBuf.prototype.writeUInt8 = function writeUInt8(v) {
  this._ensure(1);

  this.last[this.offset++] = v;
  this.avail--;
  this._move(1);
};

WBuf.prototype.writeUInt16BE = function writeUInt16BE(v) {
  this._ensure(2);

  // Fast case - everything fits into the last buffer
  if (this.avail >= 2) {
    this.last.writeUInt16BE(v, this.offset);
    this.offset += 2;
    this.avail -= 2;

  // One byte here, one byte there
  } else {
    this.last[this.offset] = (v >>> 8);
    this._next();
    this.last[this.offset++] = v & 0xff;
    this.avail--;
  }

  this._move(2);
};

WBuf.prototype.writeUInt24BE = function writeUInt24BE(v) {
  this._ensure(3);

  // Fast case - everything fits into the last buffer
  if (this.avail >= 3) {
    this.last.writeUInt16BE(v >>> 8, this.offset);
    this.last[this.offset + 2] = v & 0xff;
    this.offset += 3;
    this.avail -= 3;
    this._move(3);

  // Two bytes here
  } else if (this.avail >= 2) {
    this.last.writeUInt16BE(v >>> 8, this.offset);
    this._next();
    this.last[this.offset++] = v & 0xff;
    this.avail--;
    this._move(3);

  // Just one byte here
  } else {
    this.last[this.offset] = v >>> 16;
    this._move(1);
    this._next();
    this.writeUInt16BE(v & 0xffff);
  }
};

WBuf.prototype.writeUInt32BE = function writeUInt32BE(v) {
  this._ensure(4);

  // Fast case - everything fits into the last buffer
  if (this.avail >= 4) {
    this.last.writeUInt32BE(v, this.offset);
    this.offset += 4;
    this.avail -= 4;
    this._move(4);

  // Three bytes here
  } else if (this.avail >= 3) {
    this.writeUInt24BE(v >>> 8);
    this._next();
    this.last[this.offset++] = v & 0xff;
    this.avail--;
    this._move(1);

  // Slow case, who cares
  } else {
    this.writeUInt16BE(v >>> 16);
    this.writeUInt16BE(v & 0xffff);
  }
};

WBuf.prototype.writeUInt16LE = function writeUInt16LE(num) {
  var r = ((num & 0xff) << 8) | (num >>> 8);
  this.writeUInt16BE(r);
};

WBuf.prototype.writeUInt24LE = function writeUInt24LE(num) {
  var r = ((num & 0xff) << 16) | (((num >>> 8) & 0xff) << 8) | (num >>> 16);
  this.writeUInt24BE(r);
};

WBuf.prototype.writeUInt32LE = function writeUInt32LE(num) {
  this._ensure(4);

  // Fast case - everything fits into the last buffer
  if (this.avail >= 4) {
    this.last.writeUInt32LE(num, this.offset);
    this.offset += 4;
    this.avail -= 4;
    this._move(4);

  // Three bytes here
  } else if (this.avail >= 3) {
    this.writeUInt24LE(num & 0xffffff);
    this._next();
    this.last[this.offset++] = num >>> 24;
    this.avail--;
    this._move(1);

  // Slow case, who cares
  } else {
    this.writeUInt16LE(num & 0xffff);
    this.writeUInt16LE(num >>> 16);
  }
};

WBuf.prototype.render = function render() {
  var left = this.size;
  var out = [];

  for (var i = 0; i < this.buffers.length && left >= 0; i++) {
    var buf = this.buffers[i];
    left -= buf.length;
    if (left >= 0) {
      out.push(buf);
    } else {
      out.push(buf.slice(0, buf.length + left));
    }
  }

  return out;
};

// Signed APIs
WBuf.prototype.writeInt8 = function writeInt8(num) {
  if (num < 0)
    return this.writeUInt8(0x100 + num);
  else
    return this.writeUInt8(num);
};

function toUnsigned16(num) {
  if (num < 0)
    return 0x10000 + num;
  else
    return num;
}

WBuf.prototype.writeInt16LE = function writeInt16LE(num) {
  this.writeUInt16LE(toUnsigned16(num));
};

WBuf.prototype.writeInt16BE = function writeInt16BE(num) {
  this.writeUInt16BE(toUnsigned16(num));
};

function toUnsigned24(num) {
  if (num < 0)
    return 0x1000000 + num;
  else
    return num;
}

WBuf.prototype.writeInt24LE = function writeInt24LE(num) {
  this.writeUInt24LE(toUnsigned24(num));
};

WBuf.prototype.writeInt24BE = function writeInt24BE(num) {
  this.writeUInt24BE(toUnsigned24(num));
};

function toUnsigned32(num) {
  if (num < 0)
    return (0xffffffff + num) + 1;
  else
    return num;
}

WBuf.prototype.writeInt32LE = function writeInt32LE(num) {
  this.writeUInt32LE(toUnsigned32(num));
};

WBuf.prototype.writeInt32BE = function writeInt32BE(num) {
  this.writeUInt32BE(toUnsigned32(num));
};

WBuf.prototype.writeComb = function writeComb(size, endian, value) {
  if (size === 1)
    return this.writeUInt8(value);

  if (endian === 'le') {
    if (size === 2)
      this.writeUInt16LE(value);
    else if (size === 3)
      this.writeUInt24LE(value);
    else if (size === 4)
      this.writeUInt32LE(value);
  } else {
    if (size === 2)
      this.writeUInt16BE(value);
    else if (size === 3)
      this.writeUInt24BE(value);
    else if (size === 4)
      this.writeUInt32BE(value);
  }
};

license: mit


wbuf

description: Write buffer

test: mocha test/**/*-test.js

type: git

keywords: Write Buffer

author: Fedor Indutny <fedor@indutny.com>

license: MIT


code:
var assert = require('assert');
var WriteBuffer = require('../');

describe('WriteBuffer', function() {
  var w;
  beforeEach(function() {
    w = new WriteBuffer();
  });

  function join(arr) {
    return arr.map(function(buf) {
      return buf.toString('hex');
    }).join('');
  }

  describe('.writeUInt8', function() {
    it('should write bytes', function() {
      w.writeUInt8(1);
      w.writeUInt8(2);
      w.writeUInt8(3);
      w.writeUInt8(4);
      assert.equal(join(w.render()), '01020304');
    });

    it('should correctly handle overflow', function() {
      w.reserve(3);
      w.writeUInt8(1);
      w.writeUInt8(2);
      w.writeUInt8(3);
      w.writeUInt8(4);
      assert.equal(join(w.render()), '01020304');
    });
  });

  describe('.writeInt8', function() {
    it('should write bytes', function() {
      w.writeInt8(-1);
      w.writeInt8(2);
      assert.equal(join(w.render()), 'ff02');
    });
  });

  describe('.writeUInt16BE', function() {
    it('should write bytes', function() {
      w.writeUInt16BE(0x0102);
      w.writeUInt16BE(0x0304);
      assert.equal(join(w.render()), '01020304');
    });

    it('should correctly handle overflow', function() {
      w.reserve(2);
      w.reserve(3);
      w.writeUInt16BE(0x0102);
      w.writeUInt16BE(0x0304);
      w.writeUInt16BE(0x0506);
      assert.equal(join(w.render()), '010203040506');
    });
  });

  describe('.writeInt16BE', function() {
    it('should write bytes', function() {
      w.writeInt16BE(-0x0102);
      w.writeInt16BE(0x0304);
      assert.equal(join(w.render()), 'fefe0304');
    });
  });

  describe('.writeUInt16LE', function() {
    it('should write bytes', function() {
      w.writeUInt16LE(0x0102);
      w.writeUInt16LE(0x0304);
      assert.equal(join(w.render()), '02010403');
    });

    it('should correctly handle overflow', function() {
      w.reserve(2);
      w.reserve(3);
      w.writeUInt16LE(0x0102);
      w.writeUInt16LE(0x0304);
      w.writeUInt16LE(0x0506);
      assert.equal(join(w.render()), '020104030605');
    });
  });

  describe('.writeInt16LE', function() {
    it('should write bytes', function() {
      w.writeInt16LE(-0x0201);
      w.writeInt16LE(0x0304);
      assert.equal(join(w.render()), 'fffd0403');
    });
  });

  describe('.writeUInt24BE', function() {
    it('should write bytes', function() {
      w.writeUInt24BE(0x010203);
      w.writeUInt24BE(0x040506);
      assert.equal(join(w.render()), '010203040506');
    });

    it('should correctly set avail on boundary', function() {
      w = new WriteBuffer();
      w.reserveRate = 4;
      w.writeUInt16BE(1);
      w.writeUInt24BE(1);
      assert.equal(w.avail, 3);
    });
  });

  describe('.writeInt24BE', function() {
    it('should write bytes', function() {
      w.writeInt24BE(-0x010203);
      w.writeInt24BE(0x040506);
      assert.equal(join(w.render()), 'fefdfd040506');
    });
  });

  describe('.writeUInt24LE', function() {
    it('should write bytes', function() {
      w.writeUInt24LE(0x010203);
      w.writeUInt24LE(0x040506);
      assert.equal(join(w.render()), '030201060504');
    });
  });

  describe('.writeInt24LE', function() {
    it('should write bytes', function() {
      w.writeInt24LE(-0x010203);
      w.writeInt24LE(0x040506);
      assert.equal(join(w.render()), 'fdfdfe060504');
    });
  });

  describe('.writeUInt32BE', function() {
    it('should write bytes', function() {
      w.writeUInt32BE(0x01020304);
      w.writeUInt32BE(0x05060708);
      assert.equal(join(w.render()), '0102030405060708');
    });

    it('should write bytes on the boundary', function() {
      w.reserve(4);
      w.writeUInt8(0x00);
      w.writeUInt32BE(0x01020304);
      assert.equal(join(w.render()), '0001020304');
    });
  });

  describe('.writeInt32BE', function() {
    it('should write bytes', function() {
      w.writeInt32BE(-0x01020304);
      w.writeInt32BE(0x05060708);
      assert.equal(join(w.render()), 'fefdfcfc05060708');
    });
  });

  describe('.writeUInt32LE', function() {
    it('should write bytes', function() {
      w.writeUInt32LE(0x01020304);
      w.writeUInt32LE(0x05060708);
      assert.equal(join(w.render()), '0403020108070605');
    });

    it('should write max uint32 value', function() {
      w.writeUInt32LE(0xffffffff);
      assert.equal(join(w.render()), 'ffffffff');
    });
  });

  describe('.combWrite', function() {
    it('should write bytes', function() {
      w.writeComb(1, 'le', 0x01);
      w.writeComb(1, 'be', 0x02);
      w.writeComb(2, 'le', 0x0102);
      w.writeComb(2, 'be', 0x0304);
      w.writeComb(3, 'le', 0x010203);
      w.writeComb(3, 'be', 0x040506);
      w.writeComb(4, 'le', 0x01020304);
      w.writeComb(4, 'be', 0x05060708);
      assert.equal(join(w.render()),
                   '0102020103040302010405060403020105060708');
    });

    it('should write max uint32 value', function() {
      w.writeUInt32LE(0xffffffff);
      assert.equal(join(w.render()), 'ffffffff');
    });
  });

  describe('.writeInt32LE', function() {
    it('should write bytes', function() {
      w.writeInt32LE(-0x01020304);
      w.writeInt32LE(0x05060708);
      assert.equal(join(w.render()), 'fcfcfdfe08070605');
    });
  });

  describe('.skip', function() {
    it('should skip bytes', function() {
      w.skip(4);
      w.writeUInt32BE(0xdeadbeef);
      assert(/^.{8}deadbeef$/.test(join(w.render())));
    });

    it('should skip 0 bytes', function() {
      var skip = w.skip(0);
      assert.equal(skip.size, 0);
      w.writeUInt32BE(0xdeadbeef);
      assert(/^deadbeef$/.test(join(w.render())));
    });

    it('should skip bytes on the boundary', function() {
      w.reserve(4);
      w.writeUInt8(0x01);
      var skip = w.skip(4);
      w.writeUInt32BE(0xdeadbeef);
      skip.writeUInt32BE(0xabbabaab);
      assert(/^01abbabaabdeadbeef$/.test(join(w.render())));
    });

    it('should skip bytes on the boundary in two chunks', function() {
      w.reserve(4);
      var skip1 = w.skip(2);
      var skip2 = w.skip(2);
      w.writeUInt32BE(0xdeadbeef);
      skip1.writeUInt16BE(0xabba);
      skip2.writeUInt16BE(0xbaba);
      assert(/^abbababadeadbeef$/.test(join(w.render())));
    });
  });

  describe('.slice', function() {
    it('should return empty slice', function() {
      w.writeUInt32BE(0xabbadead);
      assert.equal(join(w.slice(4, 4).render()), '');
      assert.equal(join(w.render()), 'abbadead');
    });

    it('should return full slice', function() {
      w.writeUInt32BE(0xabbadead);
      var slice = w.slice(0, 4);
      slice.writeUInt32BE(0xdeadbeef);
      assert.equal(join(slice.render()), 'deadbeef');
      assert.equal(join(w.render()), 'deadbeef');
    });

    it('should return partial slice', function() {
      w.writeUInt32BE(0xabbadead);
      var slice = w.slice(0, 3);
      slice.writeUInt24BE(0xdeadbe);
      assert.equal(join(slice.render()), 'deadbe');
      assert.equal(join(w.render()), 'deadbead');
    });

    it('should return over-the-boundary slice', function() {
      for (var i = 0; i < 16; i++) {
        w.reserve(3);
        w.writeUInt24BE(i);
      }
      assert.equal(join(w.render()),
                   '000000000001000002000003000004000005000006000007' +
                       '00000800000900000a00000b00000c00000d00000e00000f');

      var slice = w.slice(5, 12);
      slice.writeUInt24BE(0xaaabac);
      slice.writeUInt24BE(0xbabbbc);
      slice.writeUInt8(0xcc);

      assert.equal(join(slice.render()), 'aaabacbabbbccc');
      assert.equal(join(w.render()),
                   '0000000000aaabacbabbbccc000004000005000006000007' +
                       '00000800000900000a00000b00000c00000d00000e00000f');
    });
  });

  describe('.copyFrom', function() {
    it('should copy bytes', function() {
      var tmp = new Buffer(128);
      for (var i = 0; i < tmp.length; i++)
        tmp[i] = i;
      w.writeUInt32BE(0xdeadbeef);
      w.copyFrom(tmp);
      w.writeUInt32BE(0xabbadead);

      assert.equal(
        join(w.render()),
        'deadbeef000102030405060708090a0b0c0d0e0f101112131415161718191a1b' +
        '1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b' +
        '3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b' +
        '5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b' +
        '7c7d7e7fabbadead');
    });

    it('should copy bytes using offset', function() {
      var tmp = new Buffer(128);
      for (var i = 0; i < tmp.length; i++)
        tmp[i] = i;
      w.writeUInt32BE(0xdeadbeef);
      w.copyFrom(tmp, 10, 12);
      w.writeUInt32BE(0xabbadead);

      assert.equal(
        join(w.render()),
        'deadbeef0a0babbadead');
    });
  });

  describe('.write', function() {
    it('should write utf8 string', function() {
      w.writeUInt32BE(0xdeadbeef);
      w.write('ohai\u1023');
      w.writeUInt32BE(0xabbadead);

      assert.equal(
        join(w.render()),
        'deadbeef' +
            '6f6861691023' +
            'abbadead');
    });

    it('should copy bytes using offset', function() {
      var tmp = new Buffer(128);
      for (var i = 0; i < tmp.length; i++)
        tmp[i] = i;
      w.writeUInt32BE(0xdeadbeef);
      w.copyFrom(tmp, 10, 12);
      w.writeUInt32BE(0xabbadead);

      assert.equal(
        join(w.render()),
        'deadbeef0a0babbadead');
    });
  });

  describe('.skip', function() {
    it('should copy bytes', function() {
      w.reserve(5);
      var h = w.skip(4);
      w.writeUInt32BE(0xabbadead);
      h.writeUInt32BE(0xdeadbeef);

      assert.equal(
        join(w.render()),
        'deadbeefabbadead');
    });
  });

  describe('.forceReserve = true', function() {
    it('should allocate more bytes', function() {
      w.forceReserve = true;
      w.reserve(4);
      w.writeUInt32BE(0xabbadead);
      w.writeUInt32BE(0xabbadead);

      assert.equal(w.render().length, 1);
    });
  });
});


code:
# wcwidth

Determine columns needed for a fixed-size wide-character string

----

wcwidth is a simple JavaScript port of [wcwidth](http://man7.org/linux/man-pages/man3/wcswidth.3.html) implemented in C by Markus Kuhn.

JavaScript port [originally](https://github.com/mycoboco/wcwidth.js) written by Woong Jun <woong.jun@gmail.com> (http://code.woong.org/)

## Example

```js
'한'.length    // => 1
wcwidth('한');   // => 2

'한글'.length    // => 2
wcwidth('한글'); // => 4
```

`wcwidth()` and its string version, `wcswidth()` are defined by IEEE Std
1002.1-2001, a.k.a. POSIX.1-2001, and return the number of columns used
to represent the given wide character and string.

Markus's implementation assumes the wide character given to those
functions to be encoded in ISO 10646, which is almost true for
JavaScript's characters.

[Further explaination here](docs)

## License

MIT


code:
### Javascript porting of Markus Kuhn's wcwidth() implementation

The following explanation comes from the original C implementation:

This is an implementation of wcwidth() and wcswidth() (defined in
IEEE Std 1002.1-2001) for Unicode.

http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html

In fixed-width output devices, Latin characters all occupy a single
"cell" position of equal width, whereas ideographic CJK characters
occupy two such cells. Interoperability between terminal-line
applications and (teletype-style) character terminals using the
UTF-8 encoding requires agreement on which character should advance
the cursor by how many cell positions. No established formal
standards exist at present on which Unicode character shall occupy
how many cell positions on character terminals. These routines are
a first attempt of defining such behavior based on simple rules
applied to data provided by the Unicode Consortium.

For some graphical characters, the Unicode standard explicitly
defines a character-cell width via the definition of the East Asian
FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
In all these cases, there is no ambiguity about which width a
terminal shall use. For characters in the East Asian Ambiguous (A)
class, the width choice depends purely on a preference of backward
compatibility with either historic CJK or Western practice.
Choosing single-width for these characters is easy to justify as
the appropriate long-term solution, as the CJK practice of
displaying these characters as double-width comes from historic
implementation simplicity (8-bit encoded characters were displayed
single-width and 16-bit ones double-width, even for Greek,
Cyrillic, etc.) and not any typographic considerations.

Much less clear is the choice of width for the Not East Asian
(Neutral) class. Existing practice does not dictate a width for any
of these characters. It would nevertheless make sense
typographically to allocate two character cells to characters such
as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
represented adequately with a single-width glyph. The following
routines at present merely assign a single-cell width to all
neutral characters, in the interest of simplicity. This is not
entirely satisfactory and should be reconsidered before
establishing a formal standard in this area. At the moment, the
decision which Not East Asian (Neutral) characters should be
represented by double-width glyphs cannot yet be answered by
applying a simple rule from the Unicode database content. Setting
up a proper standard for the behavior of UTF-8 character terminals
will require a careful analysis not only of each Unicode character,
but also of each presentation form, something the author of these
routines has avoided to do so far.

http://www.unicode.org/unicode/reports/tr11/

Markus Kuhn -- 2007-05-26 (Unicode 5.0)

Permission to use, copy, modify, and distribute this software
for any purpose and without fee is hereby granted. The author
disclaims all warranties with regard to this software.

Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c


code:
"use strict"

var defaults = require('defaults')
var combining = require('./combining')

var DEFAULTS = {
  nul: 0,
  control: 0
}

module.exports = function wcwidth(str) {
  return wcswidth(str, DEFAULTS)
}

module.exports.config = function(opts) {
  opts = defaults(opts || {}, DEFAULTS)
  return function wcwidth(str) {
    return wcswidth(str, opts)
  }
}

/*
 *  The following functions define the column width of an ISO 10646
 *  character as follows:
 *  - The null character (U+0000) has a column width of 0.
 *  - Other C0/C1 control characters and DEL will lead to a return value
 *    of -1.
 *  - Non-spacing and enclosing combining characters (general category
 *    code Mn or Me in the
 *    Unicode database) have a column width of 0.
 *  - SOFT HYPHEN (U+00AD) has a column width of 1.
 *  - Other format characters (general category code Cf in the Unicode
 *    database) and ZERO WIDTH
 *    SPACE (U+200B) have a column width of 0.
 *  - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
 *    have a column width of 0.
 *  - Spacing characters in the East Asian Wide (W) or East Asian
 *    Full-width (F) category as
 *    defined in Unicode Technical Report #11 have a column width of 2.
 *  - All remaining characters (including all printable ISO 8859-1 and
 *    WGL4 characters, Unicode control characters, etc.) have a column
 *    width of 1.
 *  This implementation assumes that characters are encoded in ISO 10646.
*/

function wcswidth(str, opts) {
  if (typeof str !== 'string') return wcwidth(str, opts)

  var s = 0
  for (var i = 0; i < str.length; i++) {
    var n = wcwidth(str.charCodeAt(i), opts)
    if (n < 0) return -1
    s += n
  }

  return s
}

function wcwidth(ucs, opts) {
  // test for 8-bit control characters
  if (ucs === 0) return opts.nul
  if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) return opts.control

  // binary search in table of non-spacing characters
  if (bisearch(ucs)) return 0

  // if we arrive here, ucs is not a combining or C0/C1 control character
  return 1 +
      (ucs >= 0x1100 &&
       (ucs <= 0x115f ||                       // Hangul Jamo init. consonants
        ucs == 0x2329 || ucs == 0x232a ||
        (ucs >= 0x2e80 && ucs <= 0xa4cf &&
         ucs != 0x303f) ||                     // CJK ... Yi
        (ucs >= 0xac00 && ucs <= 0xd7a3) ||    // Hangul Syllables
        (ucs >= 0xf900 && ucs <= 0xfaff) ||    // CJK Compatibility Ideographs
        (ucs >= 0xfe10 && ucs <= 0xfe19) ||    // Vertical forms
        (ucs >= 0xfe30 && ucs <= 0xfe6f) ||    // CJK Compatibility Forms
        (ucs >= 0xff00 && ucs <= 0xff60) ||    // Fullwidth Forms
        (ucs >= 0xffe0 && ucs <= 0xffe6) ||
        (ucs >= 0x20000 && ucs <= 0x2fffd) ||
        (ucs >= 0x30000 && ucs <= 0x3fffd)));
}

function bisearch(ucs) {
  var min = 0
  var max = combining.length - 1
  var mid

  if (ucs < combining[0][0] || ucs > combining[max][1]) return false

  while (max >= min) {
    mid = Math.floor((min + max) / 2)
    if (ucs > combining[mid][1]) min = mid + 1
    else if (ucs < combining[mid][0]) max = mid - 1
    else return true
  }

  return false
}


wcwidth

description: Port of C's wcwidth() and wcswidth()

author: Tim Oxley

contributors: Woong Jun <woong.jun@gmail.com> (http://code.woong.org/)

keywords:
wide character
wc
wide character string
wcs
terminal
width
wcwidth
wcswidth

doc: docs

test: test

test: tape test/*.js


code:
"use strict"

var wcwidth = require('../')
var test = require('tape')

test('handles regular strings', function(t) {
  t.strictEqual(wcwidth('abc'), 3)
  t.end()
})

test('handles multibyte strings', function(t) {
  t.strictEqual(wcwidth('字的模块'), 8)
  t.end()
})

test('handles multibyte characters mixed with regular characters', function(t) {
  t.strictEqual(wcwidth('abc 字的模块'), 12)
  t.end()
})

test('ignores control characters e.g. \\n', function(t) {
  t.strictEqual(wcwidth('abc\n字的模块\ndef'), 14)
  t.end()
})

test('ignores bad input', function(t) {
  t.strictEqual(wcwidth(''), 0)
  t.strictEqual(wcwidth(3), 0)
  t.strictEqual(wcwidth({}), 0)
  t.strictEqual(wcwidth([]), 0)
  t.strictEqual(wcwidth(), 0)
  t.end()
})

test('ignores nul (charcode 0)', function(t) {
  t.strictEqual(wcwidth(String.fromCharCode(0)), 0)
  t.end()
})

test('ignores nul mixed with chars', function(t) {
  t.strictEqual(wcwidth('a' + String.fromCharCode(0) + '\n字的'), 5)
  t.end()
})

test('can have custom value for nul', function(t) {
  t.strictEqual(wcwidth.config({
    nul: 10
  })(String.fromCharCode(0) + 'a字的'), 15)
  t.end()
})

test('can have custom control char value', function(t) {
  t.strictEqual(wcwidth.config({
    control: 1
  })('abc\n字的模块\ndef'), 16)
  t.end()
})

test('negative custom control chars == -1', function(t) {
  t.strictEqual(wcwidth.config({
    control: -1
  })('abc\n字的模块\ndef'), -1)
  t.end()
})


code:
# WebIDL Type Conversions on JavaScript Values

This package implements, in JavaScript, the algorithms to convert a given JavaScript value according to a given [WebIDL](http://heycam.github.io/webidl/) [type](http://heycam.github.io/webidl/#idl-types).

The goal is that you should be able to write code like

```js
const conversions = require("webidl-conversions");

function doStuff(x, y) {
    x = conversions["boolean"](x);
    y = conversions["unsigned long"](y);
    // actual algorithm code here
}
```

and your function `doStuff` will behave the same as a WebIDL operation declared as

```webidl
void doStuff(boolean x, unsigned long y);
```

## API

This package's main module's default export is an object with a variety of methods, each corresponding to a different WebIDL type. Each method, when invoked on a JavaScript value, will give back the new JavaScript value that results after passing through the WebIDL conversion rules. (See below for more details on what that means.) Alternately, the method could throw an error, if the WebIDL algorithm is specified to do so: for example `conversions["float"](NaN)` [will throw a `TypeError`](http://heycam.github.io/webidl/#es-float).

## Status

All of the numeric types are implemented (float being implemented as double) and some others are as well - check the source for all of them. This list will grow over time in service of the [HTML as Custom Elements](https://github.com/dglazkov/html-as-custom-elements) project, but in the meantime, pull requests welcome!

I'm not sure yet what the strategy will be for modifiers, e.g. [`[Clamp]`](http://heycam.github.io/webidl/#Clamp). Maybe something like `conversions["unsigned long"](x, { clamp: true })`? We'll see.

We might also want to extend the API to give better error messages, e.g. "Argument 1 of HTMLMediaElement.fastSeek is not a finite floating-point value" instead of "Argument is not a finite floating-point value." This would require passing in more information to the conversion functions than we currently do.

## Background

What's actually going on here, conceptually, is pretty weird. Let's try to explain.

WebIDL, as part of its madness-inducing design, has its own type system. When people write algorithms in web platform specs, they usually operate on WebIDL values, i.e. instances of WebIDL types. For example, if they were specifying the algorithm for our `doStuff` operation above, they would treat `x` as a WebIDL value of [WebIDL type `boolean`](http://heycam.github.io/webidl/#idl-boolean). Crucially, they would _not_ treat `x` as a JavaScript variable whose value is either the JavaScript `true` or `false`. They're instead working in a different type system altogether, with its own rules.

Separately from its type system, WebIDL defines a ["binding"](http://heycam.github.io/webidl/#ecmascript-binding) of the type system into JavaScript. This contains rules like: when you pass a JavaScript value to the JavaScript method that manifests a given WebIDL operation, how does that get converted into a WebIDL value? For example, a JavaScript `true` passed in the position of a WebIDL `boolean` argument becomes a WebIDL `true`. But, a JavaScript `true` passed in the position of a [WebIDL `unsigned long`](http://heycam.github.io/webidl/#idl-unsigned-long) becomes a WebIDL `1`. And so on.

Finally, we have the actual implementation code. This is usually C++, although these days [some smart people are using Rust](https://github.com/servo/servo). The implementation, of course, has its own type system. So when they implement the WebIDL algorithms, they don't actually use WebIDL values, since those aren't "real" outside of specs. Instead, implementations apply the WebIDL binding rules in such a way as to convert incoming JavaScript values into C++ values. For example, if code in the browser called `doStuff(true, true)`, then the implementation code would eventually receive a C++ `bool` containing `true` and a C++ `uint32_t` containing `1`.

The upside of all this is that implementations can abstract all the conversion logic away, letting WebIDL handle it, and focus on implementing the relevant methods in C++ with values of the correct type already provided. That is payoff of WebIDL, in a nutshell.

And getting to that payoff is the goal of _this_ project—but for JavaScript implementations, instead of C++ ones. That is, this library is designed to make it easier for JavaScript developers to write functions that behave like a given WebIDL operation. So conceptually, the conversion pipeline, which in its general form is JavaScript values ↦ WebIDL values ↦ implementation-language values, in this case becomes JavaScript values ↦ WebIDL values ↦ JavaScript values. And that intermediate step is where all the logic is performed: a JavaScript `true` becomes a WebIDL `1` in an unsigned long context, which then becomes a JavaScript `1`.

## Don't Use This

Seriously, why would you ever use this? You really shouldn't. WebIDL is … not great, and you shouldn't be emulating its semantics. If you're looking for a generic argument-processing library, you should find one with better rules than those from WebIDL. In general, your JavaScript should not be trying to become more like WebIDL; if anything, we should fix WebIDL to make it more like JavaScript.

The _only_ people who should use this are those trying to create faithful implementations (or polyfills) of web platform interfaces defined in WebIDL.


code:
"use strict";

var conversions = {};
module.exports = conversions;

function sign(x) {
    return x < 0 ? -1 : 1;
}

function evenRound(x) {
    // Round x to the nearest integer, choosing the even integer if it lies halfway between two.
    if ((x % 1) === 0.5 && (x & 1) === 0) { // [even number].5; round down (i.e. floor)
        return Math.floor(x);
    } else {
        return Math.round(x);
    }
}

function createNumberConversion(bitLength, typeOpts) {
    if (!typeOpts.unsigned) {
        --bitLength;
    }
    const lowerBound = typeOpts.unsigned ? 0 : -Math.pow(2, bitLength);
    const upperBound = Math.pow(2, bitLength) - 1;

    const moduloVal = typeOpts.moduloBitLength ? Math.pow(2, typeOpts.moduloBitLength) : Math.pow(2, bitLength);
    const moduloBound = typeOpts.moduloBitLength ? Math.pow(2, typeOpts.moduloBitLength - 1) : Math.pow(2, bitLength - 1);

    return function(V, opts) {
        if (!opts) opts = {};

        let x = +V;

        if (opts.enforceRange) {
            if (!Number.isFinite(x)) {
                throw new TypeError("Argument is not a finite number");
            }

            x = sign(x) * Math.floor(Math.abs(x));
            if (x < lowerBound || x > upperBound) {
                throw new TypeError("Argument is not in byte range");
            }

            return x;
        }

        if (!isNaN(x) && opts.clamp) {
            x = evenRound(x);

            if (x < lowerBound) x = lowerBound;
            if (x > upperBound) x = upperBound;
            return x;
        }

        if (!Number.isFinite(x) || x === 0) {
            return 0;
        }

        x = sign(x) * Math.floor(Math.abs(x));
        x = x % moduloVal;

        if (!typeOpts.unsigned && x >= moduloBound) {
            return x - moduloVal;
        } else if (typeOpts.unsigned) {
            if (x < 0) {
              x += moduloVal;
            } else if (x === -0) { // don't return negative zero
              return 0;
            }
        }

        return x;
    }
}

conversions["void"] = function () {
    return undefined;
};

conversions["boolean"] = function (val) {
    return !!val;
};

conversions["byte"] = createNumberConversion(8, { unsigned: false });
conversions["octet"] = createNumberConversion(8, { unsigned: true });

conversions["short"] = createNumberConversion(16, { unsigned: false });
conversions["unsigned short"] = createNumberConversion(16, { unsigned: true });

conversions["long"] = createNumberConversion(32, { unsigned: false });
conversions["unsigned long"] = createNumberConversion(32, { unsigned: true });

conversions["long long"] = createNumberConversion(32, { unsigned: false, moduloBitLength: 64 });
conversions["unsigned long long"] = createNumberConversion(32, { unsigned: true, moduloBitLength: 64 });

conversions["double"] = function (V) {
    const x = +V;

    if (!Number.isFinite(x)) {
        throw new TypeError("Argument is not a finite floating-point value");
    }

    return x;
};

conversions["unrestricted double"] = function (V) {
    const x = +V;

    if (isNaN(x)) {
        throw new TypeError("Argument is NaN");
    }

    return x;
};

// not quite valid, but good enough for JS
conversions["float"] = conversions["double"];
conversions["unrestricted float"] = conversions["unrestricted double"];

conversions["DOMString"] = function (V, opts) {
    if (!opts) opts = {};

    if (opts.treatNullAsEmptyString && V === null) {
        return "";
    }

    return String(V);
};

conversions["ByteString"] = function (V, opts) {
    const x = String(V);
    let c = undefined;
    for (let i = 0; (c = x.codePointAt(i)) !== undefined; ++i) {
        if (c > 255) {
            throw new TypeError("Argument is not a valid bytestring");
        }
    }

    return x;
};

conversions["USVString"] = function (V) {
    const S = String(V);
    const n = S.length;
    const U = [];
    for (let i = 0; i < n; ++i) {
        const c = S.charCodeAt(i);
        if (c < 0xD800 || c > 0xDFFF) {
            U.push(String.fromCodePoint(c));
        } else if (0xDC00 <= c && c <= 0xDFFF) {
            U.push(String.fromCodePoint(0xFFFD));
        } else {
            if (i === n - 1) {
                U.push(String.fromCodePoint(0xFFFD));
            } else {
                const d = S.charCodeAt(i + 1);
                if (0xDC00 <= d && d <= 0xDFFF) {
                    const a = c & 0x3FF;
                    const b = d & 0x3FF;
                    U.push(String.fromCodePoint((2 << 15) + (2 << 9) * a + b));
                    ++i;
                } else {
                    U.push(String.fromCodePoint(0xFFFD));
                }
            }
        }
    }

    return U.join('');
};

conversions["Date"] = function (V, opts) {
    if (!(V instanceof Date)) {
        throw new TypeError("Argument is not a Date object");
    }
    if (isNaN(V)) {
        return undefined;
    }

    return V;
};

conversions["RegExp"] = function (V, opts) {
    if (!(V instanceof RegExp)) {
        V = new RegExp(V);
    }

    return V;
};


webidl-conversions

description: Implements the WebIDL algorithms for converting to and from JavaScript values

test: mocha test/*.js

repository: jsdom/webidl-conversions

keywords: webidl web types

author: Domenic Denicola <d@domenic.me> (https://domenic.me/)


code:
[![npm][npm]][npm-url]
[![node][node]][node-url]
[![tests][tests]][tests-url]
[![downloads][downloads]][downloads-url]

Webpack Bundle Analyzer

Visualize size of webpack output files with an interactive zoomable treemap.

Install

```bash
# NPM
npm install --save-dev webpack-bundle-analyzer
# Yarn
yarn add -D webpack-bundle-analyzer
```

Usage (as a plugin)

```js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}
```

It will create an interactive treemap visualization of the contents of all your bundles.

![webpack bundle analyzer zoomable treemap](https://cloud.githubusercontent.com/assets/302213/20628702/93f72404-b338-11e6-92d4-9a365550a701.gif)

This module will help you:

1. Realize what's *really* inside your bundle
2. Find out what modules make up the most of its size
3. Find modules that got there by mistake
4. Optimize it!

And the best thing is it supports minified bundles! It parses them to get real size of bundled modules.
And it also shows their gzipped sizes!

Options (for plugin)

```js
new BundleAnalyzerPlugin(options?: object)
```

|Name|Type|Description|
|:--:|:--:|:----------|
|**`analyzerMode`**|One of: `server`, `static`, `json`, `disabled`|Default: `server`. In `server` mode analyzer will start HTTP server to show bundle report. In `static` mode single HTML file with bundle report will be generated. In `json` mode single JSON file with bundle report will be generated. In `disabled` mode you can use this plugin to just generate Webpack Stats JSON file by setting `generateStatsFile` to `true`. |
|**`analyzerHost`**|`{String}`|Default: `127.0.0.1`. Host that will be used in `server` mode to start HTTP server.|
|**`analyzerPort`**|`{Number}` or `auto`|Default: `8888`. Port that will be used in `server` mode to start HTTP server. If `analyzerPort` is `auto`, the operating system will assign an arbitrary unused port |
|**`analyzerUrl`**|`{Function}` called with `{ listenHost: string, listenHost: string, boundAddress: server.address}`. [server.address comes from Node.js](https://nodejs.org/api/net.html#serveraddress)| Default: `http://${listenHost}:${boundAddress.port}`. The URL printed to console with server mode.|
|**`reportFilename`**|`{String}`|Default: `report.html`. Path to bundle report file that will be generated in `static` mode. It can be either an absolute path or a path relative to a bundle output directory (which is output.path in webpack config).|
|**`reportTitle`**|`{String\|function}`|Default: function that returns pretty printed current date and time. Content of the HTML `title` element; or a function of the form `() => string` that provides the content.|
|**`defaultSizes`**|One of: `stat`, `parsed`, `gzip`|Default: `parsed`. Module sizes to show in report by default. [Size definitions](#size-definitions) section describes what these values mean.|
|**`openAnalyzer`**|`{Boolean}`|Default: `true`. Automatically open report in default browser.|
|**`generateStatsFile`**|`{Boolean}`|Default: `false`. If `true`, webpack stats JSON file will be generated in bundle output directory|
|**`statsFilename`**|`{String}`|Default: `stats.json`. Name of webpack stats JSON file that will be generated if `generateStatsFile` is `true`. It can be either an absolute path or a path relative to a bundle output directory (which is output.path in webpack config).|
|**`statsOptions`**|`null` or `{Object}`|Default: `null`. Options for `stats.toJson()` method. For example you can exclude sources of your modules from stats file with `source: false` option. [See more options here](https://webpack.js.org/configuration/stats/). |
|**`excludeAssets`**|`{null\|pattern\|pattern[]}` where `pattern` equals to `{String\|RegExp\|function}`|Default: `null`. Patterns that will be used to match against asset names to exclude them from the report. If pattern is a string it will be converted to RegExp via `new RegExp(str)`. If pattern is a function it should have the following signature `(assetName: string) => boolean` and should return `true` to *exclude* matching asset. If multiple patterns are provided asset should match at least one of them to be excluded. |
|**`logLevel`**|One of: `info`, `warn`, `error`, `silent`|Default: `info`. Used to control how much details the plugin outputs.|

Usage (as a CLI utility)

You can analyze an existing bundle if you have a webpack stats JSON file.

You can generate it using `BundleAnalyzerPlugin` with `generateStatsFile` option set to `true` or with this simple
command:

```bash
webpack --profile --json > stats.json
```

If you're on Windows and using PowerShell, you can generate the stats file with this command to [avoid BOM issues](https://github.com/webpack-contrib/webpack-bundle-analyzer/issues/47):

```
webpack --profile --json | Out-file 'stats.json' -Encoding OEM
```

Then you can run the CLI tool.

```
webpack-bundle-analyzer bundle/output/path/stats.json
```

Options (for CLI)

```bash
webpack-bundle-analyzer <bundleStatsFile> [bundleDir] [options]
```

Arguments are documented below:

### `bundleStatsFile`

Path to webpack stats JSON file

### `bundleDir`

Directory containing all generated bundles.

### `options`

```
  -V, --version               output the version number
  -m, --mode <mode>           Analyzer mode. Should be `server`, `static` or `json`.
                              In `server` mode analyzer will start HTTP server to show bundle report.
                              In `static` mode single HTML file with bundle report will be generated.
                              In `json` mode single JSON file with bundle report will be generated. (default: server)
  -h, --host <host>           Host that will be used in `server` mode to start HTTP server. (default: 127.0.0.1)
  -p, --port <n>              Port that will be used in `server` mode to start HTTP server. Should be a number or `auto` (default: 8888)
  -r, --report <file>         Path to bundle report file that will be generated in `static` mode. (default: report.html)
  -t, --title <title>         String to use in title element of html report. (default: pretty printed current date)
  -s, --default-sizes <type>  Module sizes to show in treemap by default.
                              Possible values: stat, parsed, gzip (default: parsed)
  -O, --no-open               Don't open report in default browser automatically.
  -e, --exclude <regexp>      Assets that should be excluded from the report.
                              Can be specified multiple times.
  -l, --log-level <level>     Log level.
                              Possible values: debug, info, warn, error, silent (default: info)
  -h, --help                  output usage information
```

Size definitions

webpack-bundle-analyzer reports three values for sizes. `defaultSizes` can be used to control which of these is shown by default. The different reported sizes are:

### `stat`

This is the "input" size of your files, before any transformations like
minification.

It is called "stat size" because it's obtained from Webpack's
[stats object](https://webpack.js.org/configuration/stats/).

### `parsed`

This is the "output" size of your files. If you're using a Webpack plugin such
as Uglify, then this value will reflect the minified size of your code.

### `gzip`

This is the size of running the parsed bundles/modules through gzip compression.

Selecting Which Chunks to Display

When opened, the report displays all of the Webpack chunks for your project. It's possible to filter to a more specific list of chunks by using the sidebar or the chunk context menu.

### Sidebar

The Sidebar Menu can be opened by clicking the `>` button at the top left of the report. You can select or deselect chunks to display under the "Show chunks" heading there.

### Chunk Context Menu

The Chunk Context Menu can be opened by right-clicking or `Ctrl`-clicking on a specific chunk in the report. It provides the following options:

 * **Hide chunk:** Hides the selected chunk
 * **Hide all other chunks:** Hides all chunks besides the selected one
 * **Show all chunks:** Un-hides any hidden chunks, returning the report to its initial, unfiltered view

Troubleshooting

### I don't see `gzip` or `parsed` sizes, it only shows `stat` size

It happens when `webpack-bundle-analyzer` analyzes files that don't actually exist in your file system, for example when you work with `webpack-dev-server` that keeps all the files in RAM. If you use `webpack-bundle-analyzer` as a plugin you won't get any errors, however if you run it via CLI you get the error message in terminal:
```
Error parsing bundle asset "your_bundle_name.bundle.js": no such file
No bundles were parsed. Analyzer will show only original module sizes from stats file.
```
To get more information about it you can read [issue #147](https://github.com/webpack-contrib/webpack-bundle-analyzer/issues/147).

Other tools

- [Statoscope](https://github.com/smelukov/statoscope/blob/master/packages/ui-webpack/README.md) - Webpack bundle analyzing tool to find out why a certain module was bundled (and more features, including interactive treemap)

Maintainers

        Yuriy Grunin

        Vesa Laakso

[npm]: https://img.shields.io/npm/v/webpack-bundle-analyzer.svg
[npm-url]: https://npmjs.com/package/webpack-bundle-analyzer

[node]: https://img.shields.io/node/v/webpack-bundle-analyzer.svg
[node-url]: https://nodejs.org

[tests]: http://img.shields.io/travis/webpack-contrib/webpack-bundle-analyzer.svg
[tests-url]: https://travis-ci.org/webpack-contrib/webpack-bundle-analyzer

[downloads]: https://img.shields.io/npm/dt/webpack-bundle-analyzer.svg
[downloads-url]: https://npmjs.com/package/webpack-bundle-analyzer

Contributing

Check out [CONTRIBUTING.md](./CONTRIBUTING.md) for instructions on contributing :tada:


code:
"use strict";

const fs = require('fs');

const path = require('path');

const {
  bold
} = require('picocolors');

const Logger = require('./Logger');

const viewer = require('./viewer');

const utils = require('./utils');

const {
  writeStats
} = require('./statsUtils');

class BundleAnalyzerPlugin {
  constructor(opts = {}) {
    this.opts = {
      analyzerMode: 'server',
      analyzerHost: '127.0.0.1',
      reportFilename: null,
      reportTitle: utils.defaultTitle,
      defaultSizes: 'parsed',
      openAnalyzer: true,
      generateStatsFile: false,
      statsFilename: 'stats.json',
      statsOptions: null,
      excludeAssets: null,
      logLevel: 'info',
      // deprecated
      startAnalyzer: true,
      analyzerUrl: utils.defaultAnalyzerUrl,
      ...opts,
      analyzerPort: 'analyzerPort' in opts ? opts.analyzerPort === 'auto' ? 0 : opts.analyzerPort : 8888
    };
    this.server = null;
    this.logger = new Logger(this.opts.logLevel);
  }

  apply(compiler) {
    this.compiler = compiler;

    const done = (stats, callback) => {
      callback = callback || (() => {});

      const actions = [];

      if (this.opts.generateStatsFile) {
        actions.push(() => this.generateStatsFile(stats.toJson(this.opts.statsOptions)));
      } // Handling deprecated `startAnalyzer` flag

      if (this.opts.analyzerMode === 'server' && !this.opts.startAnalyzer) {
        this.opts.analyzerMode = 'disabled';
      }

      if (this.opts.analyzerMode === 'server') {
        actions.push(() => this.startAnalyzerServer(stats.toJson()));
      } else if (this.opts.analyzerMode === 'static') {
        actions.push(() => this.generateStaticReport(stats.toJson()));
      } else if (this.opts.analyzerMode === 'json') {
        actions.push(() => this.generateJSONReport(stats.toJson()));
      }

      if (actions.length) {
        // Making analyzer logs to be after all webpack logs in the console
        setImmediate(async () => {
          try {
            await Promise.all(actions.map(action => action()));
            callback();
          } catch (e) {
            callback(e);
          }
        });
      } else {
        callback();
      }
    };

    if (compiler.hooks) {
      compiler.hooks.done.tapAsync('webpack-bundle-analyzer', done);
    } else {
      compiler.plugin('done', done);
    }
  }

  async generateStatsFile(stats) {
    const statsFilepath = path.resolve(this.compiler.outputPath, this.opts.statsFilename);
    await fs.promises.mkdir(path.dirname(statsFilepath), {
      recursive: true
    });

    try {
      await writeStats(stats, statsFilepath);
      this.logger.info(`${bold('Webpack Bundle Analyzer')} saved stats file to ${bold(statsFilepath)}`);
    } catch (error) {
      this.logger.error(`${bold('Webpack Bundle Analyzer')} error saving stats file to ${bold(statsFilepath)}: ${error}`);
    }
  }

  async startAnalyzerServer(stats) {
    if (this.server) {
      (await this.server).updateChartData(stats);
    } else {
      this.server = viewer.startServer(stats, {
        openBrowser: this.opts.openAnalyzer,
        host: this.opts.analyzerHost,
        port: this.opts.analyzerPort,
        reportTitle: this.opts.reportTitle,
        bundleDir: this.getBundleDirFromCompiler(),
        logger: this.logger,
        defaultSizes: this.opts.defaultSizes,
        excludeAssets: this.opts.excludeAssets,
        analyzerUrl: this.opts.analyzerUrl
      });
    }
  }

  async generateJSONReport(stats) {
    await viewer.generateJSONReport(stats, {
      reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename || 'report.json'),
      bundleDir: this.getBundleDirFromCompiler(),
      logger: this.logger,
      excludeAssets: this.opts.excludeAssets
    });
  }

  async generateStaticReport(stats) {
    await viewer.generateReport(stats, {
      openBrowser: this.opts.openAnalyzer,
      reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename || 'report.html'),
      reportTitle: this.opts.reportTitle,
      bundleDir: this.getBundleDirFromCompiler(),
      logger: this.logger,
      defaultSizes: this.opts.defaultSizes,
      excludeAssets: this.opts.excludeAssets
    });
  }

  getBundleDirFromCompiler() {
    if (typeof this.compiler.outputFileSystem.constructor === 'undefined') {
      return this.compiler.outputPath;
    }

    switch (this.compiler.outputFileSystem.constructor.name) {
      case 'MemoryFileSystem':
        return null;
      // Detect AsyncMFS used by Nuxt 2.5 that replaces webpack's MFS during development
      // Related: #274

      case 'AsyncMFS':
        return null;

      default:
        return this.compiler.outputPath;
    }
  }

}

module.exports = BundleAnalyzerPlugin;


code:
"use strict";

const LEVELS = ['debug', 'info', 'warn', 'error', 'silent'];
const LEVEL_TO_CONSOLE_METHOD = new Map([['debug', 'log'], ['info', 'log'], ['warn', 'log']]);

class Logger {
  constructor(level = Logger.defaultLevel) {
    this.activeLevels = new Set();
    this.setLogLevel(level);
  }

  setLogLevel(level) {
    const levelIndex = LEVELS.indexOf(level);
    if (levelIndex === -1) throw new Error(`Invalid log level "${level}". Use one of these: ${LEVELS.join(', ')}`);
    this.activeLevels.clear();

    for (const [i, level] of LEVELS.entries()) {
      if (i >= levelIndex) this.activeLevels.add(level);
    }
  }

  _log(level, ...args) {
    console[LEVEL_TO_CONSOLE_METHOD.get(level) || level](...args);
  }

}

Logger.levels = LEVELS;
Logger.defaultLevel = 'info';
;
LEVELS.forEach(level => {
  if (level === 'silent') return;

  Logger.prototype[level] = function (...args) {
    if (this.activeLevels.has(level)) this._log(level, ...args);
  };
});
module.exports = Logger;


code:
"use strict";

const fs = require('fs');

const path = require('path');

const gzipSize = require('gzip-size');

const {
  parseChunked
} = require('@discoveryjs/json-ext');

const Logger = require('./Logger');

const Folder = require('./tree/Folder').default;

const {
  parseBundle
} = require('./parseUtils');

const {
  createAssetsFilter
} = require('./utils');

const FILENAME_QUERY_REGEXP = /\?.*$/u;
const FILENAME_EXTENSIONS = /\.(js|mjs|cjs)$/iu;
module.exports = {
  getViewerData,
  readStatsFromFile
};

function getViewerData(bundleStats, bundleDir, opts) {
  const {
    logger = new Logger(),
    excludeAssets = null
  } = opts || {};
  const isAssetIncluded = createAssetsFilter(excludeAssets); // Sometimes all the information is located in `children` array (e.g. problem in #10)

  if ((bundleStats.assets == null || bundleStats.assets.length === 0) && bundleStats.children && bundleStats.children.length > 0) {
    const {
      children
    } = bundleStats;
    bundleStats = bundleStats.children[0]; // Sometimes if there are additional child chunks produced add them as child assets,
    // leave the 1st one as that is considered the 'root' asset.

    for (let i = 1; i < children.length; i++) {
      children[i].assets.forEach(asset => {
        asset.isChild = true;
        bundleStats.assets.push(asset);
      });
    }
  } else if (bundleStats.children && bundleStats.children.length > 0) {
    // Sometimes if there are additional child chunks produced add them as child assets
    bundleStats.children.forEach(child => {
      child.assets.forEach(asset => {
        asset.isChild = true;
        bundleStats.assets.push(asset);
      });
    });
  } // Picking only `*.js, *.cjs or *.mjs` assets from bundle that has non-empty `chunks` array

  bundleStats.assets = bundleStats.assets.filter(asset => {
    // Filter out non 'asset' type asset if type is provided (Webpack 5 add a type to indicate asset types)
    if (asset.type && asset.type !== 'asset') {
      return false;
    } // Removing query part from filename (yes, somebody uses it for some reason and Webpack supports it)
    // See #22

    asset.name = asset.name.replace(FILENAME_QUERY_REGEXP, '');
    return FILENAME_EXTENSIONS.test(asset.name) && asset.chunks.length > 0 && isAssetIncluded(asset.name);
  }); // Trying to parse bundle assets and get real module sizes if `bundleDir` is provided

  let bundlesSources = null;
  let parsedModules = null;

  if (bundleDir) {
    bundlesSources = {};
    parsedModules = {};

    for (const statAsset of bundleStats.assets) {
      const assetFile = path.join(bundleDir, statAsset.name);
      let bundleInfo;

      try {
        bundleInfo = parseBundle(assetFile);
      } catch (err) {
        const msg = err.code === 'ENOENT' ? 'no such file' : err.message;
        logger.warn(`Error parsing bundle asset "${assetFile}": ${msg}`);
        continue;
      }

      bundlesSources[statAsset.name] = {
        src: bundleInfo.src,
        runtimeSrc: bundleInfo.runtimeSrc
      };
      Object.assign(parsedModules, bundleInfo.modules);
    }

    if (Object.keys(bundlesSources).length === 0) {
      bundlesSources = null;
      parsedModules = null;
      logger.warn('\nNo bundles were parsed. Analyzer will show only original module sizes from stats file.\n');
    }
  }

  const assets = bundleStats.assets.reduce((result, statAsset) => {
    // If asset is a childAsset, then calculate appropriate bundle modules by looking through stats.children
    const assetBundles = statAsset.isChild ? getChildAssetBundles(bundleStats, statAsset.name) : bundleStats;
    const modules = assetBundles ? getBundleModules(assetBundles) : [];
    const asset = result[statAsset.name] = {
      size: statAsset.size
    };
    const assetSources = bundlesSources && Object.prototype.hasOwnProperty.call(bundlesSources, statAsset.name) ? bundlesSources[statAsset.name] : null;

    if (assetSources) {
      asset.parsedSize = Buffer.byteLength(assetSources.src);
      asset.gzipSize = gzipSize.sync(assetSources.src);
    } // Picking modules from current bundle script

    let assetModules = modules.filter(statModule => assetHasModule(statAsset, statModule)); // Adding parsed sources

    if (parsedModules) {
      const unparsedEntryModules = [];

      for (const statModule of assetModules) {
        if (parsedModules[statModule.id]) {
          statModule.parsedSrc = parsedModules[statModule.id];
        } else if (isEntryModule(statModule)) {
          unparsedEntryModules.push(statModule);
        }
      } // Webpack 5 changed bundle format and now entry modules are concatenated and located at the end of it.
      // Because of this they basically become a concatenated module, for which we can't even precisely determine its
      // parsed source as it's located in the same scope as all Webpack runtime helpers.

      if (unparsedEntryModules.length && assetSources) {
        if (unparsedEntryModules.length === 1) {
          // So if there is only one entry we consider its parsed source to be all the bundle code excluding code
          // from parsed modules.
          unparsedEntryModules[0].parsedSrc = assetSources.runtimeSrc;
        } else {
          // If there are multiple entry points we move all of them under synthetic concatenated module.
          assetModules = assetModules.filter(mod => !unparsedEntryModules.includes(mod));
          assetModules.unshift({
            identifier: './entry modules',
            name: './entry modules',
            modules: unparsedEntryModules,
            size: unparsedEntryModules.reduce((totalSize, module) => totalSize + module.size, 0),
            parsedSrc: assetSources.runtimeSrc
          });
        }
      }
    }

    asset.modules = assetModules;
    asset.tree = createModulesTree(asset.modules);
    return result;
  }, {});
  const chunkToInitialByEntrypoint = getChunkToInitialByEntrypoint(bundleStats);
  return Object.entries(assets).map(([filename, asset]) => {
    var _chunkToInitialByEntr;

    return {
      label: filename,
      isAsset: true,
      // Not using `asset.size` here provided by Webpack because it can be very confusing when `UglifyJsPlugin` is used.
      // In this case all module sizes from stats file will represent unminified module sizes, but `asset.size` will
      // be the size of minified bundle.
      // Using `asset.size` only if current asset doesn't contain any modules (resulting size equals 0)
      statSize: asset.tree.size || asset.size,
      parsedSize: asset.parsedSize,
      gzipSize: asset.gzipSize,
      groups: Object.values(asset.tree.children).map(i => i.toChartData()),
      isInitialByEntrypoint: (_chunkToInitialByEntr = chunkToInitialByEntrypoint[filename]) !== null && _chunkToInitialByEntr !== void 0 ? _chunkToInitialByEntr : {}
    };
  });
}

function readStatsFromFile(filename) {
  return parseChunked(fs.createReadStream(filename, {
    encoding: 'utf8'
  }));
}

function getChildAssetBundles(bundleStats, assetName) {
  return flatten((bundleStats.children || []).find(c => Object.values(c.assetsByChunkName))).includes(assetName);
}

function getBundleModules(bundleStats) {
  var _bundleStats$chunks;

  const seenIds = new Set();
  return flatten((((_bundleStats$chunks = bundleStats.chunks) === null || _bundleStats$chunks === void 0 ? void 0 : _bundleStats$chunks.map(chunk => chunk.modules)) || []).concat(bundleStats.modules).filter(Boolean)).filter(mod => {
    // Filtering out Webpack's runtime modules as they don't have ids and can't be parsed (introduced in Webpack 5)
    if (isRuntimeModule(mod)) {
      return false;
    }

    if (seenIds.has(mod.id)) {
      return false;
    }

    seenIds.add(mod.id);
    return true;
  });
}

function assetHasModule(statAsset, statModule) {
  // Checking if this module is the part of asset chunks
  return (statModule.chunks || []).some(moduleChunk => statAsset.chunks.includes(moduleChunk));
}

function isEntryModule(statModule) {
  return statModule.depth === 0;
}

function isRuntimeModule(statModule) {
  return statModule.moduleType === 'runtime';
}

function createModulesTree(modules) {
  const root = new Folder('.');
  modules.forEach(module => root.addModule(module));
  root.mergeNestedFolders();
  return root;
}

function getChunkToInitialByEntrypoint(bundleStats) {
  if (bundleStats == null) {
    return {};
  }

  const chunkToEntrypointInititalMap = {};
  Object.values(bundleStats.entrypoints || {}).forEach(entrypoint => {
    for (const asset of entrypoint.assets) {
      var _chunkToEntrypointIni;

      chunkToEntrypointInititalMap[asset.name] = (_chunkToEntrypointIni = chunkToEntrypointInititalMap[asset.name]) !== null && _chunkToEntrypointIni !== void 0 ? _chunkToEntrypointIni : {};
      chunkToEntrypointInititalMap[asset.name][entrypoint.name] = true;
    }
  });
  return chunkToEntrypointInititalMap;
}

;
/**
 * arr-flatten <https://github.com/jonschlinkert/arr-flatten>
 *
 * Copyright (c) 2014-2017, Jon Schlinkert.
 * Released under the MIT License.
 *
 * Modified by Sukka <https://skk.moe>
 *
 * Replace recursively flatten with one-level deep flatten to match lodash.flatten
 *
 * TODO: replace with Array.prototype.flat once Node.js 10 support is dropped
 */

function flatten(arr) {
  if (!arr) return [];
  const len = arr.length;
  if (!len) return [];
  let cur;
  const res = [];

  for (let i = 0; i < len; i++) {
    cur = arr[i];

    if (Array.isArray(cur)) {
      res.push(...cur);
    } else {
      res.push(cur);
    }
  }

  return res;
}


code:
#! /usr/bin/env node
"use strict";

const {
  resolve,
  dirname
} = require('path');

const commander = require('commander');

const {
  magenta
} = require('picocolors');

const analyzer = require('../analyzer');

const viewer = require('../viewer');

const Logger = require('../Logger');

const utils = require('../utils');

const SIZES = new Set(['stat', 'parsed', 'gzip']);
const program = commander.version(require('../../package.json').version).usage(`<bundleStatsFile> [bundleDir] [options]

  Arguments:

    bundleStatsFile  Path to Webpack Stats JSON file.
    bundleDir        Directory containing all generated bundles.
                     You should provided it if you want analyzer to show you the real parsed module sizes.
                     By default a directory of stats file is used.`).option('-m, --mode <mode>', 'Analyzer mode. Should be `server`,`static` or `json`.' + br('In `server` mode analyzer will start HTTP server to show bundle report.') + br('In `static` mode single HTML file with bundle report will be generated.') + br('In `json` mode single JSON file with bundle report will be generated.'), 'server').option( // Had to make `host` parameter optional in order to let `-h` flag output help message
// Fixes https://github.com/webpack-contrib/webpack-bundle-analyzer/issues/239
'-h, --host [host]', 'Host that will be used in `server` mode to start HTTP server.', '127.0.0.1').option('-p, --port <n>', 'Port that will be used in `server` mode to start HTTP server.', 8888).option('-r, --report <file>', 'Path to bundle report file that will be generated in `static` mode.').option('-t, --title <title>', 'String to use in title element of html report.').option('-s, --default-sizes <type>', 'Module sizes to show in treemap by default.' + br(`Possible values: ${[...SIZES].join(', ')}`), 'parsed').option('-O, --no-open', "Don't open report in default browser automatically.").option('-e, --exclude <regexp>', 'Assets that should be excluded from the report.' + br('Can be specified multiple times.'), array()).option('-l, --log-level <level>', 'Log level.' + br(`Possible values: ${[...Logger.levels].join(', ')}`), Logger.defaultLevel).parse(process.argv);
let [bundleStatsFile, bundleDir] = program.args;
let {
  mode,
  host,
  port,
  report: reportFilename,
  title: reportTitle,
  defaultSizes,
  logLevel,
  open: openBrowser,
  exclude: excludeAssets
} = program.opts();
const logger = new Logger(logLevel);

if (typeof reportTitle === 'undefined') {
  reportTitle = utils.defaultTitle;
}

if (!bundleStatsFile) showHelp('Provide path to Webpack Stats file as first argument');

if (mode !== 'server' && mode !== 'static' && mode !== 'json') {
  showHelp('Invalid mode. Should be either `server`, `static` or `json`.');
}

if (mode === 'server') {
  if (!host) showHelp('Invalid host name');
  port = port === 'auto' ? 0 : Number(port);
  if (isNaN(port)) showHelp('Invalid port. Should be a number or `auto`');
}

if (!SIZES.has(defaultSizes)) showHelp(`Invalid default sizes option. Possible values are: ${[...SIZES].join(', ')}`);
bundleStatsFile = resolve(bundleStatsFile);
if (!bundleDir) bundleDir = dirname(bundleStatsFile);
parseAndAnalyse(bundleStatsFile);

async function parseAndAnalyse(bundleStatsFile) {
  try {
    const bundleStats = await analyzer.readStatsFromFile(bundleStatsFile);

    if (mode === 'server') {
      viewer.startServer(bundleStats, {
        openBrowser,
        port,
        host,
        defaultSizes,
        reportTitle,
        bundleDir,
        excludeAssets,
        logger: new Logger(logLevel),
        analyzerUrl: utils.defaultAnalyzerUrl
      });
    } else if (mode === 'static') {
      viewer.generateReport(bundleStats, {
        openBrowser,
        reportFilename: resolve(reportFilename || 'report.html'),
        reportTitle,
        defaultSizes,
        bundleDir,
        excludeAssets,
        logger: new Logger(logLevel)
      });
    } else if (mode === 'json') {
      viewer.generateJSONReport(bundleStats, {
        reportFilename: resolve(reportFilename || 'report.json'),
        bundleDir,
        excludeAssets,
        logger: new Logger(logLevel)
      });
    }
  } catch (err) {
    logger.error(`Couldn't read webpack bundle stats from "${bundleStatsFile}":\n${err}`);
    logger.debug(err.stack);
    process.exit(1);
  }
}

function showHelp(error) {
  if (error) console.log(`\n  ${magenta(error)}\n`);
  program.outputHelp();
  process.exit(1);
}

function br(str) {
  return `\n${' '.repeat(28)}${str}`;
}

function array() {
  const arr = [];
  return val => {
    arr.push(val);
    return arr;
  };
}


code:
"use strict";

const {
  start
} = require('./viewer');

module.exports = {
  start,
  BundleAnalyzerPlugin: require('./BundleAnalyzerPlugin')
};


code:
"use strict";

const fs = require('fs');

const acorn = require('acorn');

const walk = require('acorn-walk');

module.exports = {
  parseBundle
};

function parseBundle(bundlePath) {
  const content = fs.readFileSync(bundlePath, 'utf8');
  const ast = acorn.parse(content, {
    sourceType: 'script',
    // I believe in a bright future of ECMAScript!
    // Actually, it's set to `2050` to support the latest ECMAScript version that currently exists.
    // Seems like `acorn` supports such weird option value.
    ecmaVersion: 2050
  });
  const walkState = {
    locations: null,
    expressionStatementDepth: 0
  };
  walk.recursive(ast, walkState, {
    ExpressionStatement(node, state, c) {
      if (state.locations) return;
      state.expressionStatementDepth++;

      if ( // Webpack 5 stores modules in the the top-level IIFE
      state.expressionStatementDepth === 1 && ast.body.includes(node) && isIIFE(node)) {
        const fn = getIIFECallExpression(node);

        if ( // It should not contain neither arguments
        fn.arguments.length === 0 && // ...nor parameters
        fn.callee.params.length === 0) {
          // Modules are stored in the very first variable declaration as hash
          const firstVariableDeclaration = fn.callee.body.body.find(node => node.type === 'VariableDeclaration');

          if (firstVariableDeclaration) {
            for (const declaration of firstVariableDeclaration.declarations) {
              if (declaration.init) {
                state.locations = getModulesLocations(declaration.init);

                if (state.locations) {
                  break;
                }
              }
            }
          }
        }
      }

      if (!state.locations) {
        c(node.expression, state);
      }

      state.expressionStatementDepth--;
    },

    AssignmentExpression(node, state) {
      if (state.locations) return; // Modules are stored in exports.modules:
      // exports.modules = {};

      const {
        left,
        right
      } = node;

      if (left && left.object && left.object.name === 'exports' && left.property && left.property.name === 'modules' && isModulesHash(right)) {
        state.locations = getModulesLocations(right);
      }
    },

    CallExpression(node, state, c) {
      if (state.locations) return;
      const args = node.arguments; // Main chunk with webpack loader.
      // Modules are stored in first argument:
      // (function (...) {...})(<modules>)

      if (node.callee.type === 'FunctionExpression' && !node.callee.id && args.length === 1 && isSimpleModulesList(args[0])) {
        state.locations = getModulesLocations(args[0]);
        return;
      } // Async Webpack < v4 chunk without webpack loader.
      // webpackJsonp([<chunks>], <modules>, ...)
      // As function name may be changed with `output.jsonpFunction` option we can't rely on it's default name.

      if (node.callee.type === 'Identifier' && mayBeAsyncChunkArguments(args) && isModulesList(args[1])) {
        state.locations = getModulesLocations(args[1]);
        return;
      } // Async Webpack v4 chunk without webpack loader.
      // (window.webpackJsonp=window.webpackJsonp||[]).push([[<chunks>], <modules>, ...]);
      // As function name may be changed with `output.jsonpFunction` option we can't rely on it's default name.

      if (isAsyncChunkPushExpression(node)) {
        state.locations = getModulesLocations(args[0].elements[1]);
        return;
      } // Webpack v4 WebWorkerChunkTemplatePlugin
      // globalObject.chunkCallbackName([<chunks>],<modules>, ...);
      // Both globalObject and chunkCallbackName can be changed through the config, so we can't check them.

      if (isAsyncWebWorkerChunkExpression(node)) {
        state.locations = getModulesLocations(args[1]);
        return;
      } // Walking into arguments because some of plugins (e.g. `DedupePlugin`) or some Webpack
      // features (e.g. `umd` library output) can wrap modules list into additional IIFE.

      args.forEach(arg => c(arg, state));
    }

  });
  const modules = {};

  if (walkState.locations) {
    Object.entries(walkState.locations).forEach(([id, loc]) => {
      modules[id] = content.slice(loc.start, loc.end);
    });
  }

  return {
    modules,
    src: content,
    runtimeSrc: getBundleRuntime(content, walkState.locations)
  };
}
/**
 * Returns bundle source except modules
 */

function getBundleRuntime(content, modulesLocations) {
  const sortedLocations = Object.values(modulesLocations || {}).sort((a, b) => a.start - b.start);
  let result = '';
  let lastIndex = 0;

  for (const {
    start,
    end
  } of sortedLocations) {
    result += content.slice(lastIndex, start);
    lastIndex = end;
  }

  return result + content.slice(lastIndex, content.length);
}

function isIIFE(node) {
  return node.type === 'ExpressionStatement' && (node.expression.type === 'CallExpression' || node.expression.type === 'UnaryExpression' && node.expression.argument.type === 'CallExpression');
}

function getIIFECallExpression(node) {
  if (node.expression.type === 'UnaryExpression') {
    return node.expression.argument;
  } else {
    return node.expression;
  }
}

function isModulesList(node) {
  return isSimpleModulesList(node) || // Modules are contained in expression `Array([minimum ID]).concat([<module>, <module>, ...])`
  isOptimizedModulesArray(node);
}

function isSimpleModulesList(node) {
  return (// Modules are contained in hash. Keys are module ids.
    isModulesHash(node) || // Modules are contained in array. Indexes are module ids.
    isModulesArray(node)
  );
}

function isModulesHash(node) {
  return node.type === 'ObjectExpression' && node.properties.map(node => node.value).every(isModuleWrapper);
}

function isModulesArray(node) {
  return node.type === 'ArrayExpression' && node.elements.every(elem => // Some of array items may be skipped because there is no module with such id
  !elem || isModuleWrapper(elem));
}

function isOptimizedModulesArray(node) {
  // Checking whether modules are contained in `Array(<minimum ID>).concat(...modules)` array:
  // https://github.com/webpack/webpack/blob/v1.14.0/lib/Template.js#L91
  // The `<minimum ID>` + array indexes are module ids
  return node.type === 'CallExpression' && node.callee.type === 'MemberExpression' && // Make sure the object called is `Array(<some number>)`
  node.callee.object.type === 'CallExpression' && node.callee.object.callee.type === 'Identifier' && node.callee.object.callee.name === 'Array' && node.callee.object.arguments.length === 1 && isNumericId(node.callee.object.arguments[0]) && // Make sure the property X called for `Array(<some number>).X` is `concat`
  node.callee.property.type === 'Identifier' && node.callee.property.name === 'concat' && // Make sure exactly one array is passed in to `concat`
  node.arguments.length === 1 && isModulesArray(node.arguments[0]);
}

function isModuleWrapper(node) {
  return (// It's an anonymous function expression that wraps module
    (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') && !node.id || // If `DedupePlugin` is used it can be an ID of duplicated module...
    isModuleId(node) || // or an array of shape [<module_id>, ...args]
    node.type === 'ArrayExpression' && node.elements.length > 1 && isModuleId(node.elements[0])
  );
}

function isModuleId(node) {
  return node.type === 'Literal' && (isNumericId(node) || typeof node.value === 'string');
}

function isNumericId(node) {
  return node.type === 'Literal' && Number.isInteger(node.value) && node.value >= 0;
}

function isChunkIds(node) {
  // Array of numeric or string ids. Chunk IDs are strings when NamedChunksPlugin is used
  return node.type === 'ArrayExpression' && node.elements.every(isModuleId);
}

function isAsyncChunkPushExpression(node) {
  const {
    callee,
    arguments: args
  } = node;
  return callee.type === 'MemberExpression' && callee.property.name === 'push' && callee.object.type === 'AssignmentExpression' && args.length === 1 && args[0].type === 'ArrayExpression' && mayBeAsyncChunkArguments(args[0].elements) && isModulesList(args[0].elements[1]);
}

function mayBeAsyncChunkArguments(args) {
  return args.length >= 2 && isChunkIds(args[0]);
}

function isAsyncWebWorkerChunkExpression(node) {
  const {
    callee,
    type,
    arguments: args
  } = node;
  return type === 'CallExpression' && callee.type === 'MemberExpression' && args.length === 2 && isChunkIds(args[0]) && isModulesList(args[1]);
}

function getModulesLocations(node) {
  if (node.type === 'ObjectExpression') {
    // Modules hash
    const modulesNodes = node.properties;
    return modulesNodes.reduce((result, moduleNode) => {
      const moduleId = moduleNode.key.name || moduleNode.key.value;
      result[moduleId] = getModuleLocation(moduleNode.value);
      return result;
    }, {});
  }

  const isOptimizedArray = node.type === 'CallExpression';

  if (node.type === 'ArrayExpression' || isOptimizedArray) {
    // Modules array or optimized array
    const minId = isOptimizedArray ? // Get the [minId] value from the Array() call first argument literal value
    node.callee.object.arguments[0].value : // `0` for simple array
    0;
    const modulesNodes = isOptimizedArray ? // The modules reside in the `concat()` function call arguments
    node.arguments[0].elements : node.elements;
    return modulesNodes.reduce((result, moduleNode, i) => {
      if (moduleNode) {
        result[i + minId] = getModuleLocation(moduleNode);
      }

      return result;
    }, {});
  }

  return {};
}

function getModuleLocation(node) {
  return {
    start: node.start,
    end: node.end
  };
}


code:
"use strict";

const {
  createWriteStream
} = require('fs');

const {
  Readable
} = require('stream');

class StatsSerializeStream extends Readable {
  constructor(stats) {
    super();
    this._indentLevel = 0;
    this._stringifier = this._stringify(stats);
  }

  get _indent() {
    return '  '.repeat(this._indentLevel);
  }

  _read() {
    let readMore = true;

    while (readMore) {
      const {
        value,
        done
      } = this._stringifier.next();

      if (done) {
        this.push(null);
        readMore = false;
      } else {
        readMore = this.push(value);
      }
    }
  }

  *_stringify(obj) {
    if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || obj === null) {
      yield JSON.stringify(obj);
    } else if (Array.isArray(obj)) {
      yield '[';
      this._indentLevel++;
      let isFirst = true;

      for (let item of obj) {
        if (item === undefined) {
          item = null;
        }

        yield `${isFirst ? '' : ','}\n${this._indent}`;
        yield* this._stringify(item);
        isFirst = false;
      }

      this._indentLevel--;
      yield obj.length ? `\n${this._indent}]` : ']';
    } else {
      yield '{';
      this._indentLevel++;
      let isFirst = true;
      const entries = Object.entries(obj);

      for (const [itemKey, itemValue] of entries) {
        if (itemValue === undefined) {
          continue;
        }

        yield `${isFirst ? '' : ','}\n${this._indent}${JSON.stringify(itemKey)}: `;
        yield* this._stringify(itemValue);
        isFirst = false;
      }

      this._indentLevel--;
      yield entries.length ? `\n${this._indent}}` : '}';
    }
  }

}

exports.StatsSerializeStream = StatsSerializeStream;
exports.writeStats = writeStats;

async function writeStats(stats, filepath) {
  return new Promise((resolve, reject) => {
    new StatsSerializeStream(stats).on('end', resolve).on('error', reject).pipe(createWriteStream(filepath));
  });
}


code:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _Node = _interopRequireDefault(require("./Node"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class BaseFolder extends _Node.default {
  constructor(name, parent) {
    super(name, parent);
    this.children = Object.create(null);
  }

  get src() {
    if (!Object.prototype.hasOwnProperty.call(this, '_src')) {
      this._src = this.walk((node, src) => src += node.src || '', '', false);
    }

    return this._src;
  }

  get size() {
    if (!Object.prototype.hasOwnProperty.call(this, '_size')) {
      this._size = this.walk((node, size) => size + node.size, 0, false);
    }

    return this._size;
  }

  getChild(name) {
    return this.children[name];
  }

  addChildModule(module) {
    const {
      name
    } = module;
    const currentChild = this.children[name]; // For some reason we already have this node in children and it's a folder.

    if (currentChild && currentChild instanceof BaseFolder) return;

    if (currentChild) {
      // We already have this node in children and it's a module.
      // Merging it's data.
      currentChild.mergeData(module.data);
    } else {
      // Pushing new module
      module.parent = this;
      this.children[name] = module;
    }

    delete this._size;
    delete this._src;
  }

  addChildFolder(folder) {
    folder.parent = this;
    this.children[folder.name] = folder;
    delete this._size;
    delete this._src;
    return folder;
  }

  walk(walker, state = {}, deep = true) {
    let stopped = false;
    Object.values(this.children).forEach(child => {
      if (deep && child.walk) {
        state = child.walk(walker, state, stop);
      } else {
        state = walker(child, state, stop);
      }

      if (stopped) return false;
    });
    return state;

    function stop(finalState) {
      stopped = true;
      return finalState;
    }
  }

  mergeNestedFolders() {
    if (!this.isRoot) {
      let childNames;

      while ((childNames = Object.keys(this.children)).length === 1) {
        const childName = childNames[0];
        const onlyChild = this.children[childName];

        if (onlyChild instanceof this.constructor) {
          this.name += `/${onlyChild.name}`;
          this.children = onlyChild.children;
        } else {
          break;
        }
      }
    }

    this.walk(child => {
      child.parent = this;

      if (child.mergeNestedFolders) {
        child.mergeNestedFolders();
      }
    }, null, false);
  }

  toChartData() {
    return {
      label: this.name,
      path: this.path,
      statSize: this.size,
      groups: Object.values(this.children).map(child => child.toChartData())
    };
  }

}

exports.default = BaseFolder;
;


code:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _Module = _interopRequireDefault(require("./Module"));

var _ContentModule = _interopRequireDefault(require("./ContentModule"));

var _ContentFolder = _interopRequireDefault(require("./ContentFolder"));

var _utils = require("./utils");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class ConcatenatedModule extends _Module.default {
  constructor(name, data, parent) {
    super(name, data, parent);
    this.name += ' (concatenated)';
    this.children = Object.create(null);
    this.fillContentModules();
  }

  get parsedSize() {
    var _this$getParsedSize;

    return (_this$getParsedSize = this.getParsedSize()) !== null && _this$getParsedSize !== void 0 ? _this$getParsedSize : this.getEstimatedSize('parsedSize');
  }

  get gzipSize() {
    var _this$getGzipSize;

    return (_this$getGzipSize = this.getGzipSize()) !== null && _this$getGzipSize !== void 0 ? _this$getGzipSize : this.getEstimatedSize('gzipSize');
  }

  getEstimatedSize(sizeType) {
    const parentModuleSize = this.parent[sizeType];

    if (parentModuleSize !== undefined) {
      return Math.floor(this.size / this.parent.size * parentModuleSize);
    }
  }

  fillContentModules() {
    this.data.modules.forEach(moduleData => this.addContentModule(moduleData));
  }

  addContentModule(moduleData) {
    const pathParts = (0, _utils.getModulePathParts)(moduleData);

    if (!pathParts) {
      return;
    }

    const [folders, fileName] = [pathParts.slice(0, -1), pathParts[pathParts.length - 1]];
    let currentFolder = this;
    folders.forEach(folderName => {
      let childFolder = currentFolder.getChild(folderName);

      if (!childFolder) {
        childFolder = currentFolder.addChildFolder(new _ContentFolder.default(folderName, this));
      }

      currentFolder = childFolder;
    });
    const ModuleConstructor = moduleData.modules ? ConcatenatedModule : _ContentModule.default;
    const module = new ModuleConstructor(fileName, moduleData, this);
    currentFolder.addChildModule(module);
  }

  getChild(name) {
    return this.children[name];
  }

  addChildModule(module) {
    module.parent = this;
    this.children[module.name] = module;
  }

  addChildFolder(folder) {
    folder.parent = this;
    this.children[folder.name] = folder;
    return folder;
  }

  mergeNestedFolders() {
    Object.values(this.children).forEach(child => {
      if (child.mergeNestedFolders) {
        child.mergeNestedFolders();
      }
    });
  }

  toChartData() {
    return { ...super.toChartData(),
      concatenated: true,
      groups: Object.values(this.children).map(child => child.toChartData())
    };
  }

}

exports.default = ConcatenatedModule;
;


code:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _BaseFolder = _interopRequireDefault(require("./BaseFolder"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class ContentFolder extends _BaseFolder.default {
  constructor(name, ownerModule, parent) {
    super(name, parent);
    this.ownerModule = ownerModule;
  }

  get parsedSize() {
    return this.getSize('parsedSize');
  }

  get gzipSize() {
    return this.getSize('gzipSize');
  }

  getSize(sizeType) {
    const ownerModuleSize = this.ownerModule[sizeType];

    if (ownerModuleSize !== undefined) {
      return Math.floor(this.size / this.ownerModule.size * ownerModuleSize);
    }
  }

  toChartData() {
    return { ...super.toChartData(),
      parsedSize: this.parsedSize,
      gzipSize: this.gzipSize,
      inaccurateSizes: true
    };
  }

}

exports.default = ContentFolder;
;


code:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _Module = _interopRequireDefault(require("./Module"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class ContentModule extends _Module.default {
  constructor(name, data, ownerModule, parent) {
    super(name, data, parent);
    this.ownerModule = ownerModule;
  }

  get parsedSize() {
    return this.getSize('parsedSize');
  }

  get gzipSize() {
    return this.getSize('gzipSize');
  }

  getSize(sizeType) {
    const ownerModuleSize = this.ownerModule[sizeType];

    if (ownerModuleSize !== undefined) {
      return Math.floor(this.size / this.ownerModule.size * ownerModuleSize);
    }
  }

  toChartData() {
    return { ...super.toChartData(),
      inaccurateSizes: true
    };
  }

}

exports.default = ContentModule;
;


code:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _gzipSize = _interopRequireDefault(require("gzip-size"));

var _Module = _interopRequireDefault(require("./Module"));

var _BaseFolder = _interopRequireDefault(require("./BaseFolder"));

var _ConcatenatedModule = _interopRequireDefault(require("./ConcatenatedModule"));

var _utils = require("./utils");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class Folder extends _BaseFolder.default {
  get parsedSize() {
    return this.src ? this.src.length : 0;
  }

  get gzipSize() {
    if (!Object.prototype.hasOwnProperty.call(this, '_gzipSize')) {
      this._gzipSize = this.src ? _gzipSize.default.sync(this.src) : 0;
    }

    return this._gzipSize;
  }

  addModule(moduleData) {
    const pathParts = (0, _utils.getModulePathParts)(moduleData);

    if (!pathParts) {
      return;
    }

    const [folders, fileName] = [pathParts.slice(0, -1), pathParts[pathParts.length - 1]];
    let currentFolder = this;
    folders.forEach(folderName => {
      let childNode = currentFolder.getChild(folderName);

      if ( // Folder is not created yet
      !childNode || // In some situations (invalid usage of dynamic `require()`) webpack generates a module with empty require
      // context, but it's moduleId points to a directory in filesystem.
      // In this case we replace this `File` node with `Folder`.
      // See `test/stats/with-invalid-dynamic-require.json` as an example.
      !(childNode instanceof Folder)) {
        childNode = currentFolder.addChildFolder(new Folder(folderName));
      }

      currentFolder = childNode;
    });
    const ModuleConstructor = moduleData.modules ? _ConcatenatedModule.default : _Module.default;
    const module = new ModuleConstructor(fileName, moduleData, this);
    currentFolder.addChildModule(module);
  }

  toChartData() {
    return { ...super.toChartData(),
      parsedSize: this.parsedSize,
      gzipSize: this.gzipSize
    };
  }

}

exports.default = Folder;
;


code:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _gzipSize = _interopRequireDefault(require("gzip-size"));

var _Node = _interopRequireDefault(require("./Node"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

class Module extends _Node.default {
  constructor(name, data, parent) {
    super(name, parent);
    this.data = data;
  }

  get src() {
    return this.data.parsedSrc;
  }

  set src(value) {
    this.data.parsedSrc = value;
    delete this._gzipSize;
  }

  get size() {
    return this.data.size;
  }

  set size(value) {
    this.data.size = value;
  }

  get parsedSize() {
    return this.getParsedSize();
  }

  get gzipSize() {
    return this.getGzipSize();
  }

  getParsedSize() {
    return this.src ? this.src.length : undefined;
  }

  getGzipSize() {
    if (!('_gzipSize' in this)) {
      this._gzipSize = this.src ? _gzipSize.default.sync(this.src) : undefined;
    }

    return this._gzipSize;
  }

  mergeData(data) {
    if (data.size) {
      this.size += data.size;
    }

    if (data.parsedSrc) {
      this.src = (this.src || '') + data.parsedSrc;
    }
  }

  toChartData() {
    return {
      id: this.data.id,
      label: this.name,
      path: this.path,
      statSize: this.size,
      parsedSize: this.parsedSize,
      gzipSize: this.gzipSize
    };
  }

}

exports.default = Module;
;


code:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

class Node {
  constructor(name, parent) {
    this.name = name;
    this.parent = parent;
  }

  get path() {
    const path = [];
    let node = this;

    while (node) {
      path.push(node.name);
      node = node.parent;
    }

    return path.reverse().join('/');
  }

  get isRoot() {
    return !this.parent;
  }

}

exports.default = Node;
;


code:
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getModulePathParts = getModulePathParts;
const MULTI_MODULE_REGEXP = /^multi /u;

function getModulePathParts(moduleData) {
  if (MULTI_MODULE_REGEXP.test(moduleData.identifier)) {
    return [moduleData.identifier];
  }

  const loaders = moduleData.name.split('!'); // Removing loaders from module path: they're joined by `!` and the last part is a raw module path

  const parsedPath = loaders[loaders.length - 1] // Splitting module path into parts
  .split('/') // Removing first `.`
  .slice(1) // Replacing `~` with `node_modules`
  .map(part => part === '~' ? 'node_modules' : part);
  return parsedPath.length ? parsedPath : null;
}


code:
"use strict";

const {
  inspect,
  types
} = require('util');

const opener = require('opener');

const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
exports.createAssetsFilter = createAssetsFilter;

function createAssetsFilter(excludePatterns) {
  const excludeFunctions = (Array.isArray(excludePatterns) ? excludePatterns : [excludePatterns]).filter(Boolean).map(pattern => {
    if (typeof pattern === 'string') {
      pattern = new RegExp(pattern, 'u');
    }

    if (types.isRegExp(pattern)) {
      return asset => pattern.test(asset);
    }

    if (typeof pattern !== 'function') {
      throw new TypeError(`Pattern should be either string, RegExp or a function, but "${inspect(pattern, {
        depth: 0
      })}" got.`);
    }

    return pattern;
  });

  if (excludeFunctions.length) {
    return asset => excludeFunctions.every(fn => fn(asset) !== true);
  } else {
    return () => true;
  }
}
/**
 * @desc get string of current time
 * format: dd/MMM HH:mm
 * */

exports.defaultTitle = function () {
  const time = new Date();
  const year = time.getFullYear();
  const month = MONTHS[time.getMonth()];
  const day = time.getDate();
  const hour = `0${time.getHours()}`.slice(-2);
  const minute = `0${time.getMinutes()}`.slice(-2);
  const currentTime = `${day} ${month} ${year} at ${hour}:${minute}`;
  return `${process.env.npm_package_name || 'Webpack Bundle Analyzer'} [${currentTime}]`;
};

exports.defaultAnalyzerUrl = function (options) {
  const {
    listenHost,
    boundAddress
  } = options;
  return `http://${listenHost}:${boundAddress.port}`;
};
/**
 * Calls opener on a URI, but silently try / catches it.
 */

exports.open = function (uri, logger) {
  try {
    opener(uri);
  } catch (err) {
    logger.debug(`Opener failed to open "${uri}":\n${err}`);
  }
};