UNPKG

ember-material-icons

Version:

Google Material icons for your ember-cli app

195 lines (169 loc) 5.5 kB
import visitorKeys from '../types/visitor-keys'; import { cannotRemoveNode, cannotReplaceNode, cannotReplaceOrRemoveInKeyHandlerYet } from './errors'; import * as nodes from '../types/nodes'; export interface NodeVisitor { All?: NodeHandler<nodes.BaseNode>; Program?: NodeHandler<nodes.Program>; MustacheStatement?: NodeHandler<nodes.MustacheStatement>; BlockStatement?: NodeHandler<nodes.BlockStatement>; ElementModifierStatement?: NodeHandler<nodes.ElementModifierStatement>; PartialStatement?: NodeHandler<nodes.PartialStatement>; CommentStatement?: NodeHandler<nodes.CommentStatement>; MustacheCommentStatement?: NodeHandler<nodes.MustacheCommentStatement>; ElementNode?: NodeHandler<nodes.ElementNode>; AttrNode?: NodeHandler<nodes.AttrNode>; TextNode?: NodeHandler<nodes.TextNode>; ConcatStatement?: NodeHandler<nodes.ConcatStatement>; SubExpression?: NodeHandler<nodes.SubExpression>; PathExpression?: NodeHandler<nodes.PathExpression>; StringLiteral?: NodeHandler<nodes.StringLiteral>; BooleanLiteral?: NodeHandler<nodes.BooleanLiteral>; NumberLiteral?: NodeHandler<nodes.NumberLiteral>; UndefinedLiteral?: NodeHandler<nodes.UndefinedLiteral>; NullLiteral?: NodeHandler<nodes.NullLiteral>; Hash?: NodeHandler<nodes.Hash>; HashPair?: NodeHandler<nodes.HashPair>; } export type NodeHandler<T> = NodeHandlerFunction<T> | EnterExitNodeHandler<T>; export interface NodeHandlerFunction<T> { (node: T): any; } export interface EnterExitNodeHandler<T> { enter: NodeHandlerFunction<T>; exit: NodeHandlerFunction<T>; keys: any; } function visitNode(visitor, node: nodes.BaseNode) { let handler = visitor[node.type] || visitor.All; let result; if (handler && handler.enter) { result = handler.enter.call(null, node); } if (result !== undefined && result !== null) { if (JSON.stringify(node) === JSON.stringify(result)) { result = undefined; } else if (Array.isArray(result)) { return visitArray(visitor, result) || result; } else { return visitNode(visitor, result) || result; } } if (result === undefined) { let keys = visitorKeys[node.type]; for (let i = 0; i < keys.length; i++) { visitKey(visitor, handler, node, keys[i]); } if (handler && handler.exit) { result = handler.exit.call(null, node); } } return result; } function visitKey(visitor, handler, node: nodes.BaseNode, key) { let value = node[key]; if (!value) { return; } let keyHandler = handler && (handler.keys[key] || handler.keys.All); let result; if (keyHandler && keyHandler.enter) { result = keyHandler.enter.call(null, node, key); if (result !== undefined) { throw cannotReplaceOrRemoveInKeyHandlerYet(node, key); } } if (Array.isArray(value)) { visitArray(visitor, value); } else { let result = visitNode(visitor, value); if (result !== undefined) { assignKey(node, key, result); } } if (keyHandler && keyHandler.exit) { result = keyHandler.exit.call(null, node, key); if (result !== undefined) { throw cannotReplaceOrRemoveInKeyHandlerYet(node, key); } } } function visitArray(visitor, array) { for (let i = 0; i < array.length; i++) { let result = visitNode(visitor, array[i]); if (result !== undefined) { i += spliceArray(array, i, result) - 1; } } } function assignKey(node: nodes.BaseNode, key, result) { if (result === null) { throw cannotRemoveNode(node[key], node, key); } else if (Array.isArray(result)) { if (result.length === 1) { node[key] = result[0]; } else { if (result.length === 0) { throw cannotRemoveNode(node[key], node, key); } else { throw cannotReplaceNode(node[key], node, key); } } } else { node[key] = result; } } function spliceArray(array, index, result) { if (result === null) { array.splice(index, 1); return 0; } else if (Array.isArray(result)) { array.splice(index, 1, ...result); return result.length; } else { array.splice(index, 1, result); return 1; } } export default function traverse(node: nodes.BaseNode, visitor: NodeVisitor) { visitNode(normalizeVisitor(visitor), node); } export function normalizeVisitor(visitor: NodeVisitor) { let normalizedVisitor = {}; for (let type in visitor) { let handler = visitor[type] || visitor.All; let normalizedKeys = {}; if (typeof handler === 'object') { let keys = handler.keys; if (keys) { for (let key in keys) { let keyHandler = keys[key]; if (typeof keyHandler === 'object') { normalizedKeys[key] = { enter: (typeof keyHandler.enter === 'function') ? keyHandler.enter : null, exit: (typeof keyHandler.exit === 'function') ? keyHandler.exit : null }; } else if (typeof keyHandler === 'function') { normalizedKeys[key] = { enter: keyHandler, exit: null }; } } } normalizedVisitor[type] = { enter: (typeof handler.enter === 'function') ? handler.enter : null, exit: (typeof handler.exit === 'function') ? handler.exit : null, keys: normalizedKeys }; } else if (typeof handler === 'function') { normalizedVisitor[type] = { enter: handler, exit: null, keys: normalizedKeys }; } } return normalizedVisitor; }