UNPKG

makestatic-core

Version:

Generic file processing library

193 lines (180 loc) 4.62 kB
/** * Abstraction for abstract syntax tree parsers and serializers that * provides a common API for interacting with abstract syntax trees. * * This implementation provides a mechanism for lazily converting buffers to * strings when the tree is parsed and lazily parsing when the `result` * property is accessed. * * It also allows for trees to be marked as dirty, if a tree has not been * marked as dirty and `serialize` is called the original content is returned. * * @class TreeAdapter */ class TreeAdapter { /** * Create a TreeAdapter. * * All constructor parameters are required. * * @constructor TreeAdapter * @param {Function} parse a closure that parses to an AST. * @param {Function} serialize a closure that serializes an AST * @param {Function} iterator a closure that iterates the AST * @param {String|Buffer} content the content to parse. * @param {Function} [clone] called when this AST adapter is cloned. */ constructor (parse, serialize, iterator, content, clone) { this._dirty = false this._parse = parse this._serialize = serialize this._iterator = iterator this._content = content this._clone = clone } /** * Calls the underlying AST parse closure and sets the result on * this adapter. * * @function parse * @member TreeAdapter * * @returns this tree adapter. */ parse (...args) { this._result = this._parse(this, this.content, ...args) return this } /** * Calls the underlying AST serialize closure. * * @function serialize * @member TreeAdapter * * @returns the serialize closure return value. */ serialize (...args) { return this._serialize(this, this.result, ...args) } /** * Gets a copy of this tree adapter sharing the same parse, serialize * iterator and clone closures. * * If the consumer passed a `clone` function to the constructor it is called * with the cloned tree adapter so that the consumer can decorate the new * tree if necessary. * * @function clone * @member TreeAdapter * @param {String} [content] content for the new tree adapter. * * @returns a new tree adapter. */ clone (content) { const copy = new TreeAdapter( this._parse, this._serialize, this._iterator, content || this._content, this._clone) if (this._clone) { this._clone(copy) } return copy } /** * Gets a file seal closure for this adapter. * * If this AST is not dirty the original content is returned. * * @function getSeal * @member TreeAdapter * * @returns a seal closure. */ getSeal () { return () => { if (!this.dirty) { return this.content } return this.serialize() } } /** * Calls a function passing the result AST and flags this tree as dirty. * * If the passed function does not return a value it is assumed the tree is * dirty otherwise the return value is coerced to a boolean and set as the * dirty flag. * * @function modify * @member TreeAdapter * @param {Function} fn the tree modifier function. * * @returns this tree adapter. */ modify (fn) { const res = fn(this.result) this._dirty = (res === undefined) || Boolean(res) return this } /** * Walk the abstract syntax tree nodes. * * @function walk * @member TreeAdapter * @param {Function} predicate test if a node should be visited. * @param {Function} fn visit a node. * * @returns this tree adapter. */ walk (predicate, fn) { const it = this._iterator it(this, this.result, (node, index) => { if (predicate(node, index)) { return fn(node, index) } }, it) return this } /** * Source file content. * * @property {Buffer|String} content * @member TreeAdapter * @readonly */ get content () { if (Buffer.isBuffer(this._content)) { this._content = this._content.toString() } return this._content } /** * A parse result object. * * @property {Object} result * @member TreeAdapter * @readonly */ get result () { // lazily parse when we need a result object if (this._result === undefined) { this.parse() } return this._result } /** * Determines if the tree is dirty. * * @property {Boolean} dirty * @member TreeAdapter */ get dirty () { return this._dirty } set dirty (val) { this._dirty = val } } module.exports = TreeAdapter