UNPKG

superchild

Version:

A smarter replacement for node.js's child_process module.

204 lines (132 loc) 9.38 kB
<!DOCTYPE html> <html> <head> <title>json-sieve.js</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="public/stylesheets/normalize.css" /> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div class="container"> <div class="page"> <div class="header"> <h1>json-sieve.js</h1> <div class="toc"> <h3>Table of Contents</h3> <ol> <li> <a class="source" href="json-sieve.html"> json-sieve.js </a> </li> <li> <a class="source" href="superchild.html"> superchild.js </a> </li> <li> <a class="source" href="unlogger.html"> unlogger.js </a> </li> </ol> </div> </div> <h2 id="jsonsieve">JSONSieve</h2> <p>Picks out line-delimited JSON arrays and objects in a data stream, and parses the rest into lines.</p> <h3 id="usage">Usage</h3> <pre><code><span class="hljs-keyword">var</span> sieve = <span class="hljs-keyword">new</span> JSONSieve(); process.stdin.on(<span class="hljs-string">'data'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">chunk</span>) </span>{ sieve.observe(chunk); }); process.stdin.on(<span class="hljs-string">'end'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ sieve.close(); <span class="hljs-comment">// important</span> }); sieve.on(<span class="hljs-string">'stdout_line'</span>, ...); sieve.on(<span class="hljs-string">'stderr_data'</span>, ...); sieve.on(<span class="hljs-string">'json_object'</span>, ...); sieve.on(<span class="hljs-string">'json_array'</span>, ...); </code></pre><p><code>JSONSieve</code> is an <code>EventEmitter</code>.</p> <p><strong>Warning:</strong> Note that JSON will not be parsed if it is “pretty-printed” with whitespace.</p> <h3 id="source">Source</h3> <div class='highlight'><pre><span class="hljs-keyword">var</span> EventEmitter = <span class="hljs-built_in">require</span>(<span class="hljs-string">'events'</span>) , util = <span class="hljs-built_in">require</span>(<span class="hljs-string">'util'</span>) ; <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">JSONSieve</span>(<span class="hljs-params"></span>) </span>{ EventEmitter.call(<span class="hljs-keyword">this</span>); <span class="hljs-keyword">this</span>.lineBuffer_ = <span class="hljs-string">''</span>; } util.inherits(JSONSieve, EventEmitter);</pre></div> <p>After construction, chunked data may be passed to <code>observe()</code>.</p> <div class='highlight'><pre>JSONSieve.prototype.observe = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">dataStr</span>) </span>{ <span class="hljs-keyword">this</span>.lineBuffer_ += dataStr; <span class="hljs-keyword">this</span>.parseLineBuffer_(); };</pre></div> <p>On every chunk, we add the new data to the existing line buffer, and attempting to find newlines to split on. If there are no newlines, then assume that the last chunk we got was a partial line.</p> <div class='highlight'><pre>JSONSieve.prototype.parseLineBuffer_ = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-keyword">var</span> lines = []; <span class="hljs-keyword">var</span> dataStr = <span class="hljs-keyword">this</span>.lineBuffer_; <span class="hljs-keyword">var</span> idx = dataStr.indexOf(<span class="hljs-string">'\n'</span>); <span class="hljs-keyword">while</span>(idx &gt;= <span class="hljs-number">0</span>) { lines.push(dataStr.substr(<span class="hljs-number">0</span>, idx)); <span class="hljs-comment">// Excludes terminating new line.</span> dataStr = dataStr.substr(idx + <span class="hljs-number">1</span>); <span class="hljs-comment">// Skip newline.</span> idx = dataStr.indexOf(<span class="hljs-string">'\n'</span>); } <span class="hljs-keyword">this</span>.lineBuffer_ = dataStr; <span class="hljs-comment">// Linebuffer contains leftovers.</span> <span class="hljs-keyword">if</span> (lines.length) { lines.forEach(<span class="hljs-keyword">this</span>.processLine_, <span class="hljs-keyword">this</span>); } };</pre></div> <p>For every line parsed out of the stream, attempt to decode JSON strings and arrays. A decision is made here not to attempt to decode JSON strings and numbers, because they are too accidentally common in most output streams.</p> <div class='highlight'><pre>JSONSieve.prototype.processLine_ = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">line</span>) </span>{</pre></div> <p>Allow some fuzz in the input in the form of leading and trailing whitespace.</p> <div class='highlight'><pre> <span class="hljs-keyword">var</span> trimmedLine = line.replace(<span class="hljs-regexp">/^[\s\u0000-\u001f]*|[\s\u0000-\u001f]*$/g</span>, <span class="hljs-string">''</span>);</pre></div> <p>Check for necessary conditions for line-delimited JSON objects and arrays, even empty ones.</p> <div class='highlight'><pre> <span class="hljs-keyword">var</span> len = trimmedLine.length; <span class="hljs-keyword">if</span> (len &gt;= <span class="hljs-number">2</span>) {</pre></div> <p>Test for possible line-delimited JSON objects.</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> (trimmedLine[<span class="hljs-number">0</span>] === <span class="hljs-string">'{'</span> &amp;&amp; trimmedLine[len - <span class="hljs-number">1</span>] === <span class="hljs-string">'}'</span>) { <span class="hljs-keyword">var</span> jsonObj; <span class="hljs-keyword">try</span> { jsonObj = <span class="hljs-built_in">JSON</span>.parse(trimmedLine); } <span class="hljs-keyword">catch</span>(e) { } <span class="hljs-comment">// Nope, not a JSON object.</span> <span class="hljs-keyword">if</span> (jsonObj &amp;&amp; <span class="hljs-keyword">typeof</span> jsonObj == <span class="hljs-string">'object'</span>) { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.emit(<span class="hljs-string">'json_object'</span>, jsonObj); } }</pre></div> <p>Test for possible line-delimited JSON arrays.</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> (trimmedLine[<span class="hljs-number">0</span>] === <span class="hljs-string">'['</span> &amp;&amp; trimmedLine[len - <span class="hljs-number">1</span>] === <span class="hljs-string">']'</span>) { <span class="hljs-keyword">var</span> jsonArr; <span class="hljs-keyword">try</span> { jsonArr = <span class="hljs-built_in">JSON</span>.parse(trimmedLine); } <span class="hljs-keyword">catch</span>(e) { } <span class="hljs-comment">// Nope, not a JSON array.</span> <span class="hljs-keyword">if</span> (jsonArr &amp;&amp; <span class="hljs-keyword">typeof</span> jsonArr == <span class="hljs-string">'object'</span> &amp;&amp; <span class="hljs-string">'length'</span> <span class="hljs-keyword">in</span> jsonArr) { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.emit(<span class="hljs-string">'json_array'</span>, jsonArr); } } }</pre></div> <p>Emit as a raw line.</p> <div class='highlight'><pre> <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.emit(<span class="hljs-string">'stdout_line'</span>, line); };</pre></div> <p>Finish up any remaining data remaining in the line buffer, usually the last line of output if it does not have a terminating newline.</p> <div class='highlight'><pre>JSONSieve.prototype.close = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">cb</span>) </span>{ <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span>.lineBuffer_) { <span class="hljs-keyword">this</span>.processLine_(<span class="hljs-keyword">this</span>.lineBuffer_); <span class="hljs-keyword">delete</span> <span class="hljs-keyword">this</span>.lineBuffer_; } <span class="hljs-keyword">return</span> (cb &amp;&amp; cb()); }; <span class="hljs-built_in">module</span>.exports = JSONSieve;</pre></div> <div class="fleur">h</div> </div> </div> </body> </html>