work.suroh.tk/node_modules/node-source-walk/index.js

131 lines
3.1 KiB
JavaScript

const parser = require('@babel/parser');
/**
* @param {Object} options - Options to configure parser
* @param {Object} options.parser - An object with a parse method that returns an AST
*/
module.exports = function(options = {}) {
this.parser = options.parser || parser;
if (options.parser) {
// We don't want to send that down to the actual parser
delete options.parser;
}
this.options = Object.assign({
plugins: [
'jsx',
'flow',
'doExpressions',
'objectRestSpread',
['decorators', {decoratorsBeforeExport: true}],
'classProperties',
'exportDefaultFrom',
'exportNamespaceFrom',
'asyncGenerators',
'functionBind',
'functionSent',
'dynamicImport',
'optionalChaining',
'nullishCoalescingOperator'
],
allowHashBang: true,
sourceType: 'module'
}, options);
// We use global state to stop the recursive traversal of the AST
this.shouldStop = false;
};
/**
* @param {String} src
* @param {Object} [options] - Parser options
* @return {Object} The AST of the given src
*/
module.exports.prototype.parse = function(src, options) {
options = options || this.options;
// Keep around for consumers of parse that supply their own options
if (typeof options.allowHashBang === 'undefined') {
options.allowHashBang = true;
}
return this.parser.parse(src, options);
};
/**
* Adapted from substack/node-detective
* Executes cb on a non-array AST node
*/
module.exports.prototype.traverse = function(node, cb) {
if (this.shouldStop) { return; }
if (Array.isArray(node)) {
for (let i = 0, l = node.length; i < l; i++) {
const x = node[i];
if (x !== null) {
// Mark that the node has been visited
x.parent = node;
this.traverse(x, cb);
}
}
} else if (node && typeof node === 'object') {
cb(node);
for (let key in node) {
// Avoid visited nodes
if (key === 'parent' || !node[key]) { continue; }
node[key].parent = node;
this.traverse(node[key], cb);
}
}
};
/**
* Executes the passed callback for every traversed node of
* the passed in src's ast
*
* @param {String|Object} src - The source code or AST to traverse
* @param {Function} cb - Called for every node
*/
module.exports.prototype.walk = function(src, cb) {
this.shouldStop = false;
const ast = typeof src === 'object' ? src : this.parse(src);
this.traverse(ast, cb);
};
module.exports.prototype.moonwalk = function(node, cb) {
this.shouldStop = false;
if (typeof node !== 'object') {
throw new Error('node must be an object');
}
reverseTraverse.call(this, node, cb);
};
function reverseTraverse(node, cb) {
if (this.shouldStop || !node.parent) { return; }
if (node.parent instanceof Array) {
for (let i = 0, l = node.parent.length; i < l; i++) {
cb(node.parent[i]);
}
} else {
cb(node.parent);
}
reverseTraverse.call(this, node.parent, cb);
}
/**
* Halts further traversal of the AST
*/
module.exports.prototype.stopWalking = function() {
this.shouldStop = true;
};