| ; | |
| var identity = require('./nodes/identity.js'); | |
| const BREAK = Symbol('break visit'); | |
| const SKIP = Symbol('skip children'); | |
| const REMOVE = Symbol('remove node'); | |
| /** | |
| * Apply a visitor to an AST node or document. | |
| * | |
| * Walks through the tree (depth-first) starting from `node`, calling a | |
| * `visitor` function with three arguments: | |
| * - `key`: For sequence values and map `Pair`, the node's index in the | |
| * collection. Within a `Pair`, `'key'` or `'value'`, correspondingly. | |
| * `null` for the root node. | |
| * - `node`: The current node. | |
| * - `path`: The ancestry of the current node. | |
| * | |
| * The return value of the visitor may be used to control the traversal: | |
| * - `undefined` (default): Do nothing and continue | |
| * - `visit.SKIP`: Do not visit the children of this node, continue with next | |
| * sibling | |
| * - `visit.BREAK`: Terminate traversal completely | |
| * - `visit.REMOVE`: Remove the current node, then continue with the next one | |
| * - `Node`: Replace the current node, then continue by visiting it | |
| * - `number`: While iterating the items of a sequence or map, set the index | |
| * of the next step. This is useful especially if the index of the current | |
| * node has changed. | |
| * | |
| * If `visitor` is a single function, it will be called with all values | |
| * encountered in the tree, including e.g. `null` values. Alternatively, | |
| * separate visitor functions may be defined for each `Map`, `Pair`, `Seq`, | |
| * `Alias` and `Scalar` node. To define the same visitor function for more than | |
| * one node type, use the `Collection` (map and seq), `Value` (map, seq & scalar) | |
| * and `Node` (alias, map, seq & scalar) targets. Of all these, only the most | |
| * specific defined one will be used for each node. | |
| */ | |
| function visit(node, visitor) { | |
| const visitor_ = initVisitor(visitor); | |
| if (identity.isDocument(node)) { | |
| const cd = visit_(null, node.contents, visitor_, Object.freeze([node])); | |
| if (cd === REMOVE) | |
| node.contents = null; | |
| } | |
| else | |
| visit_(null, node, visitor_, Object.freeze([])); | |
| } | |
| // Without the `as symbol` casts, TS declares these in the `visit` | |
| // namespace using `var`, but then complains about that because | |
| // `unique symbol` must be `const`. | |
| /** Terminate visit traversal completely */ | |
| visit.BREAK = BREAK; | |
| /** Do not visit the children of the current node */ | |
| visit.SKIP = SKIP; | |
| /** Remove the current node */ | |
| visit.REMOVE = REMOVE; | |
| function visit_(key, node, visitor, path) { | |
| const ctrl = callVisitor(key, node, visitor, path); | |
| if (identity.isNode(ctrl) || identity.isPair(ctrl)) { | |
| replaceNode(key, path, ctrl); | |
| return visit_(key, ctrl, visitor, path); | |
| } | |
| if (typeof ctrl !== 'symbol') { | |
| if (identity.isCollection(node)) { | |
| path = Object.freeze(path.concat(node)); | |
| for (let i = 0; i < node.items.length; ++i) { | |
| const ci = visit_(i, node.items[i], visitor, path); | |
| if (typeof ci === 'number') | |
| i = ci - 1; | |
| else if (ci === BREAK) | |
| return BREAK; | |
| else if (ci === REMOVE) { | |
| node.items.splice(i, 1); | |
| i -= 1; | |
| } | |
| } | |
| } | |
| else if (identity.isPair(node)) { | |
| path = Object.freeze(path.concat(node)); | |
| const ck = visit_('key', node.key, visitor, path); | |
| if (ck === BREAK) | |
| return BREAK; | |
| else if (ck === REMOVE) | |
| node.key = null; | |
| const cv = visit_('value', node.value, visitor, path); | |
| if (cv === BREAK) | |
| return BREAK; | |
| else if (cv === REMOVE) | |
| node.value = null; | |
| } | |
| } | |
| return ctrl; | |
| } | |
| /** | |
| * Apply an async visitor to an AST node or document. | |
| * | |
| * Walks through the tree (depth-first) starting from `node`, calling a | |
| * `visitor` function with three arguments: | |
| * - `key`: For sequence values and map `Pair`, the node's index in the | |
| * collection. Within a `Pair`, `'key'` or `'value'`, correspondingly. | |
| * `null` for the root node. | |
| * - `node`: The current node. | |
| * - `path`: The ancestry of the current node. | |
| * | |
| * The return value of the visitor may be used to control the traversal: | |
| * - `Promise`: Must resolve to one of the following values | |
| * - `undefined` (default): Do nothing and continue | |
| * - `visit.SKIP`: Do not visit the children of this node, continue with next | |
| * sibling | |
| * - `visit.BREAK`: Terminate traversal completely | |
| * - `visit.REMOVE`: Remove the current node, then continue with the next one | |
| * - `Node`: Replace the current node, then continue by visiting it | |
| * - `number`: While iterating the items of a sequence or map, set the index | |
| * of the next step. This is useful especially if the index of the current | |
| * node has changed. | |
| * | |
| * If `visitor` is a single function, it will be called with all values | |
| * encountered in the tree, including e.g. `null` values. Alternatively, | |
| * separate visitor functions may be defined for each `Map`, `Pair`, `Seq`, | |
| * `Alias` and `Scalar` node. To define the same visitor function for more than | |
| * one node type, use the `Collection` (map and seq), `Value` (map, seq & scalar) | |
| * and `Node` (alias, map, seq & scalar) targets. Of all these, only the most | |
| * specific defined one will be used for each node. | |
| */ | |
| async function visitAsync(node, visitor) { | |
| const visitor_ = initVisitor(visitor); | |
| if (identity.isDocument(node)) { | |
| const cd = await visitAsync_(null, node.contents, visitor_, Object.freeze([node])); | |
| if (cd === REMOVE) | |
| node.contents = null; | |
| } | |
| else | |
| await visitAsync_(null, node, visitor_, Object.freeze([])); | |
| } | |
| // Without the `as symbol` casts, TS declares these in the `visit` | |
| // namespace using `var`, but then complains about that because | |
| // `unique symbol` must be `const`. | |
| /** Terminate visit traversal completely */ | |
| visitAsync.BREAK = BREAK; | |
| /** Do not visit the children of the current node */ | |
| visitAsync.SKIP = SKIP; | |
| /** Remove the current node */ | |
| visitAsync.REMOVE = REMOVE; | |
| async function visitAsync_(key, node, visitor, path) { | |
| const ctrl = await callVisitor(key, node, visitor, path); | |
| if (identity.isNode(ctrl) || identity.isPair(ctrl)) { | |
| replaceNode(key, path, ctrl); | |
| return visitAsync_(key, ctrl, visitor, path); | |
| } | |
| if (typeof ctrl !== 'symbol') { | |
| if (identity.isCollection(node)) { | |
| path = Object.freeze(path.concat(node)); | |
| for (let i = 0; i < node.items.length; ++i) { | |
| const ci = await visitAsync_(i, node.items[i], visitor, path); | |
| if (typeof ci === 'number') | |
| i = ci - 1; | |
| else if (ci === BREAK) | |
| return BREAK; | |
| else if (ci === REMOVE) { | |
| node.items.splice(i, 1); | |
| i -= 1; | |
| } | |
| } | |
| } | |
| else if (identity.isPair(node)) { | |
| path = Object.freeze(path.concat(node)); | |
| const ck = await visitAsync_('key', node.key, visitor, path); | |
| if (ck === BREAK) | |
| return BREAK; | |
| else if (ck === REMOVE) | |
| node.key = null; | |
| const cv = await visitAsync_('value', node.value, visitor, path); | |
| if (cv === BREAK) | |
| return BREAK; | |
| else if (cv === REMOVE) | |
| node.value = null; | |
| } | |
| } | |
| return ctrl; | |
| } | |
| function initVisitor(visitor) { | |
| if (typeof visitor === 'object' && | |
| (visitor.Collection || visitor.Node || visitor.Value)) { | |
| return Object.assign({ | |
| Alias: visitor.Node, | |
| Map: visitor.Node, | |
| Scalar: visitor.Node, | |
| Seq: visitor.Node | |
| }, visitor.Value && { | |
| Map: visitor.Value, | |
| Scalar: visitor.Value, | |
| Seq: visitor.Value | |
| }, visitor.Collection && { | |
| Map: visitor.Collection, | |
| Seq: visitor.Collection | |
| }, visitor); | |
| } | |
| return visitor; | |
| } | |
| function callVisitor(key, node, visitor, path) { | |
| if (typeof visitor === 'function') | |
| return visitor(key, node, path); | |
| if (identity.isMap(node)) | |
| return visitor.Map?.(key, node, path); | |
| if (identity.isSeq(node)) | |
| return visitor.Seq?.(key, node, path); | |
| if (identity.isPair(node)) | |
| return visitor.Pair?.(key, node, path); | |
| if (identity.isScalar(node)) | |
| return visitor.Scalar?.(key, node, path); | |
| if (identity.isAlias(node)) | |
| return visitor.Alias?.(key, node, path); | |
| return undefined; | |
| } | |
| function replaceNode(key, path, node) { | |
| const parent = path[path.length - 1]; | |
| if (identity.isCollection(parent)) { | |
| parent.items[key] = node; | |
| } | |
| else if (identity.isPair(parent)) { | |
| if (key === 'key') | |
| parent.key = node; | |
| else | |
| parent.value = node; | |
| } | |
| else if (identity.isDocument(parent)) { | |
| parent.contents = node; | |
| } | |
| else { | |
| const pt = identity.isAlias(parent) ? 'alias' : 'scalar'; | |
| throw new Error(`Cannot replace node with ${pt} parent`); | |
| } | |
| } | |
| exports.visit = visit; | |
| exports.visitAsync = visitAsync; | |
Xet Storage Details
- Size:
- 9.25 kB
- Xet hash:
- 609717d1577d498a38d225aa3b7e0a5e58c5a9c60091184dc70bdfe102038035
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.