Skip to content

Commit

Permalink
Merge a36f846 into 711bb1f
Browse files Browse the repository at this point in the history
  • Loading branch information
yoshinorin committed Jul 28, 2022
2 parents 711bb1f + a36f846 commit c6f1857
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 4 deletions.
4 changes: 2 additions & 2 deletions lib/database.js
@@ -1,6 +1,6 @@
'use strict';

const JSONStream = require('JSONStream');
const { parse: createJsonParseStream } = require('./jsonstream');
const Promise = require('bluebird');
const fs = require('graceful-fs');
const Model = require('./model');
Expand Down Expand Up @@ -134,7 +134,7 @@ class Database {
};

// data event arg0 wrap key/value pair.
const parseStream = JSONStream.parse('models.$*');
const parseStream = createJsonParseStream('models.$*');

parseStream.once('header', getMetaCallBack);
parseStream.once('footer', getMetaCallBack);
Expand Down
202 changes: 202 additions & 0 deletions lib/jsonstream/index.js
@@ -0,0 +1,202 @@
'use strict';

const through2 = require('through2');
const Parser = require('jsonparse');

/**
* Check whether a x and y are equal, or x matches y, or x(y) is truthy.
* @param {boolean | string | RegExp | (args: any[]) => boolean} x
* @param {*} y
* @returns {boolean}
*/
const check = (x, y) => {
if (typeof x === 'string') {
return y === x;
}

if (x && typeof x.exec === 'function') {
return x.exec(y);
}

if (typeof x === 'boolean' || typeof x === 'object') {
return x;
}

if (typeof x === 'function') {
return x(y);
}

return false;
};

module.exports.parse = function(path, map) {
let header, footer;

const parser = new Parser();
const stream = through2.obj(
(chunk, enc, cb) => {
if (typeof chunk === 'string') {
chunk = Buffer.from(chunk);
}
parser.write(chunk);
cb();
},
cb => {
if (header) {
stream.emit('header', header);
}
if (footer) {
stream.emit('footer', footer);
}

if (parser.tState !== Parser.C.START || parser.stack.length > 0) {
cb(new Error('Incomplete JSON'));
return;
}

cb();
}
);

if (typeof path === 'string') {
path = path.split('.').map(e => {
if (e === '$*') {
return { emitKey: true };
}
if (e === '*') {
return true;
}
if (e === '') {
// '..'.split('.') returns an empty string
return { recurse: true };
}
return e;
});
}


if (!path || !path.length) {
path = null;
}

parser.onValue = function(value) {
if (!this.root) { stream.root = value; }

if (!path) return;

let i = 0; // iterates on path
let j = 0; // iterates on stack
let emitKey = false;
let emitPath = false;
while (i < path.length) {
const key = path[i];
let c;
j++;

if (key && !key.recurse) {
c = j === this.stack.length ? this : this.stack[j];
if (!c) return;
if (!check(key, c.key)) {
setHeaderFooter(c.key, value);
return;
}
emitKey = !!key.emitKey;
emitPath = !!key.emitPath;
i++;
} else {
i++;
const nextKey = path[i];
if (!nextKey) return;

// eslint-disable-next-line no-constant-condition
while (true) {
c = j === this.stack.length ? this : this.stack[j];
if (!c) return;
if (check(nextKey, c.key)) {
i++;
if (!Object.isFrozen(this.stack[j])) {
this.stack[j].value = null;
}
break;
} else {
setHeaderFooter(c.key, value);
}
j++;
}
}

}

// emit header
if (header) {
stream.emit('header', header);
header = false;
}
if (j !== this.stack.length) return;

const actualPath = this.stack.slice(1).map(element => element.key);
actualPath.push(this.key);

let data = this.value[this.key];
if (data != null) {
if ((data = map ? map(data, actualPath) : data) != null) {
if (emitKey || emitPath) {
data = {
value: data
};
if (emitKey) {
data.key = this.key;
}
if (emitPath) {
data.path = actualPath;
}
}

stream.push(data);
}
}

delete this.value[this.key];

for (const k in this.stack) {
if (!Object.isFrozen(this.stack[k])) {
this.stack[k].value = null;
}
}
};
parser._onToken = parser.onToken;

parser.onToken = function(token, value) {
parser._onToken(token, value);
if (this.stack.length === 0) {
if (stream.root) {
if (!path) { stream.push(stream.root); }

stream.root = null;
}
}
};

parser.onError = function(err) {
if (err.message.includes('at position')) {
err.message = 'Invalid JSON (' + err.message + ')';
}
stream.destroy(err);
};

return stream;

function setHeaderFooter(key, value) {
// header has not been emitted yet
if (header !== false) {
header = header || {};
header[key] = value;
}

// footer has not been emitted yet but header has
if (footer !== false && header === false) {
footer = footer || {};
footer[key] = value;
}
}
};
5 changes: 3 additions & 2 deletions package.json
Expand Up @@ -29,8 +29,9 @@
"graceful-fs": "^4.1.3",
"hexo-log": "^3.0.0",
"is-plain-object": "^5.0.0",
"JSONStream": "^1.0.7",
"rfdc": "^1.1.4"
"jsonparse": "^1.3.1",
"rfdc": "^1.1.4",
"through2": "^4.0.2"
},
"devDependencies": {
"chai": "^4.2.0",
Expand Down

0 comments on commit c6f1857

Please sign in to comment.