Assembler.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. 'use strict';
  2. const EventEmitter = require('events');
  3. const startObject = Ctr =>
  4. function () {
  5. if (this.done) {
  6. this.done = false;
  7. } else {
  8. this.stack.push(this.current, this.key);
  9. }
  10. this.current = new Ctr();
  11. this.key = null;
  12. };
  13. class Assembler extends EventEmitter {
  14. static connectTo(stream, options) {
  15. return new Assembler(options).connectTo(stream);
  16. }
  17. constructor(options) {
  18. super();
  19. this.stack = [];
  20. this.current = this.key = null;
  21. this.done = true;
  22. if (options) {
  23. this.reviver = typeof options.reviver == 'function' && options.reviver;
  24. if (this.reviver) {
  25. this.stringValue = this._saveValue = this._saveValueWithReviver;
  26. }
  27. if (options.numberAsString) {
  28. this.numberValue = this.stringValue;
  29. }
  30. }
  31. }
  32. connectTo(stream) {
  33. stream.on('data', chunk => {
  34. if (this[chunk.name]) {
  35. this[chunk.name](chunk.value);
  36. if (this.done) this.emit('done', this);
  37. }
  38. });
  39. return this;
  40. }
  41. get depth() {
  42. return (this.stack.length >> 1) + (this.done ? 0 : 1);
  43. }
  44. get path() {
  45. const path = [];
  46. for (let i = 0; i < this.stack.length; i += 2) {
  47. const key = this.stack[i + 1];
  48. path.push(key === null ? this.stack[i].length : key);
  49. }
  50. return path;
  51. }
  52. dropToLevel(level) {
  53. if (level < this.depth) {
  54. if (level) {
  55. const index = (level - 1) << 1;
  56. this.current = this.stack[index];
  57. this.key = this.stack[index + 1];
  58. this.stack.splice(index);
  59. } else {
  60. this.stack = [];
  61. this.current = this.key = null;
  62. this.done = true;
  63. }
  64. }
  65. return this;
  66. }
  67. consume(chunk) {
  68. this[chunk.name] && this[chunk.name](chunk.value);
  69. return this;
  70. }
  71. keyValue(value) {
  72. this.key = value;
  73. }
  74. //stringValue() - aliased below to _saveValue()
  75. numberValue(value) {
  76. this._saveValue(parseFloat(value));
  77. }
  78. nullValue() {
  79. this._saveValue(null);
  80. }
  81. trueValue() {
  82. this._saveValue(true);
  83. }
  84. falseValue() {
  85. this._saveValue(false);
  86. }
  87. //startObject() - assigned below
  88. endObject() {
  89. if (this.stack.length) {
  90. const value = this.current;
  91. this.key = this.stack.pop();
  92. this.current = this.stack.pop();
  93. this._saveValue(value);
  94. } else {
  95. this.done = true;
  96. }
  97. }
  98. //startArray() - assigned below
  99. //endArray() - aliased below to endObject()
  100. _saveValue(value) {
  101. if (this.done) {
  102. this.current = value;
  103. } else {
  104. if (this.current instanceof Array) {
  105. this.current.push(value);
  106. } else {
  107. this.current[this.key] = value;
  108. this.key = null;
  109. }
  110. }
  111. }
  112. _saveValueWithReviver(value) {
  113. if (this.done) {
  114. this.current = this.reviver('', value);
  115. } else {
  116. if (this.current instanceof Array) {
  117. value = this.reviver('' + this.current.length, value);
  118. this.current.push(value);
  119. if (value === undefined) {
  120. delete this.current[this.current.length - 1];
  121. }
  122. } else {
  123. value = this.reviver(this.key, value);
  124. if (value !== undefined) {
  125. this.current[this.key] = value;
  126. }
  127. this.key = null;
  128. }
  129. }
  130. }
  131. }
  132. Assembler.prototype.stringValue = Assembler.prototype._saveValue;
  133. Assembler.prototype.startObject = startObject(Object);
  134. Assembler.prototype.startArray = startObject(Array);
  135. Assembler.prototype.endArray = Assembler.prototype.endObject;
  136. module.exports = Assembler;