diff options
| author | Adam Mathes <adam@adammathes.com> | 2026-02-13 21:34:48 -0800 |
|---|---|---|
| committer | Adam Mathes <adam@adammathes.com> | 2026-02-13 21:34:48 -0800 |
| commit | 76cb9c2a39d477a64824a985ade40507e3bbade1 (patch) | |
| tree | 41e997aa9c6f538d3a136af61dae9424db2005a9 /vanilla/node_modules/css-tree/cjs/walker/create.cjs | |
| parent | 819a39a21ac992b1393244a4c283bbb125208c69 (diff) | |
| download | neko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.gz neko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.bz2 neko-76cb9c2a39d477a64824a985ade40507e3bbade1.zip | |
feat(vanilla): add testing infrastructure and tests (NK-wjnczv)
Diffstat (limited to 'vanilla/node_modules/css-tree/cjs/walker/create.cjs')
| -rw-r--r-- | vanilla/node_modules/css-tree/cjs/walker/create.cjs | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/vanilla/node_modules/css-tree/cjs/walker/create.cjs b/vanilla/node_modules/css-tree/cjs/walker/create.cjs new file mode 100644 index 0000000..3938a9f --- /dev/null +++ b/vanilla/node_modules/css-tree/cjs/walker/create.cjs @@ -0,0 +1,291 @@ +'use strict'; + +const { hasOwnProperty } = Object.prototype; +const noop = function() {}; + +function ensureFunction(value) { + return typeof value === 'function' ? value : noop; +} + +function invokeForType(fn, type) { + return function(node, item, list) { + if (node.type === type) { + fn.call(this, node, item, list); + } + }; +} + +function getWalkersFromStructure(name, nodeType) { + const structure = nodeType.structure; + const walkers = []; + + for (const key in structure) { + if (hasOwnProperty.call(structure, key) === false) { + continue; + } + + let fieldTypes = structure[key]; + const walker = { + name: key, + type: false, + nullable: false + }; + + if (!Array.isArray(fieldTypes)) { + fieldTypes = [fieldTypes]; + } + + for (const fieldType of fieldTypes) { + if (fieldType === null) { + walker.nullable = true; + } else if (typeof fieldType === 'string') { + walker.type = 'node'; + } else if (Array.isArray(fieldType)) { + walker.type = 'list'; + } + } + + if (walker.type) { + walkers.push(walker); + } + } + + if (walkers.length) { + return { + context: nodeType.walkContext, + fields: walkers + }; + } + + return null; +} + +function getTypesFromConfig(config) { + const types = {}; + + for (const name in config.node) { + if (hasOwnProperty.call(config.node, name)) { + const nodeType = config.node[name]; + + if (!nodeType.structure) { + throw new Error('Missed `structure` field in `' + name + '` node type definition'); + } + + types[name] = getWalkersFromStructure(name, nodeType); + } + } + + return types; +} + +function createTypeIterator(config, reverse) { + const fields = config.fields.slice(); + const contextName = config.context; + const useContext = typeof contextName === 'string'; + + if (reverse) { + fields.reverse(); + } + + return function(node, context, walk, walkReducer) { + let prevContextValue; + + if (useContext) { + prevContextValue = context[contextName]; + context[contextName] = node; + } + + for (const field of fields) { + const ref = node[field.name]; + + if (!field.nullable || ref) { + if (field.type === 'list') { + const breakWalk = reverse + ? ref.reduceRight(walkReducer, false) + : ref.reduce(walkReducer, false); + + if (breakWalk) { + return true; + } + } else if (walk(ref)) { + return true; + } + } + } + + if (useContext) { + context[contextName] = prevContextValue; + } + }; +} + +function createFastTraveralMap({ + StyleSheet, + Atrule, + Rule, + Block, + DeclarationList +}) { + return { + Atrule: { + StyleSheet, + Atrule, + Rule, + Block + }, + Rule: { + StyleSheet, + Atrule, + Rule, + Block + }, + Declaration: { + StyleSheet, + Atrule, + Rule, + Block, + DeclarationList + } + }; +} + +function createWalker(config) { + const types = getTypesFromConfig(config); + const iteratorsNatural = {}; + const iteratorsReverse = {}; + const breakWalk = Symbol('break-walk'); + const skipNode = Symbol('skip-node'); + + for (const name in types) { + if (hasOwnProperty.call(types, name) && types[name] !== null) { + iteratorsNatural[name] = createTypeIterator(types[name], false); + iteratorsReverse[name] = createTypeIterator(types[name], true); + } + } + + const fastTraversalIteratorsNatural = createFastTraveralMap(iteratorsNatural); + const fastTraversalIteratorsReverse = createFastTraveralMap(iteratorsReverse); + + const walk = function(root, options) { + function walkNode(node, item, list) { + const enterRet = enter.call(context, node, item, list); + + if (enterRet === breakWalk) { + return true; + } + + if (enterRet === skipNode) { + return false; + } + + if (iterators.hasOwnProperty(node.type)) { + if (iterators[node.type](node, context, walkNode, walkReducer)) { + return true; + } + } + + if (leave.call(context, node, item, list) === breakWalk) { + return true; + } + + return false; + } + + let enter = noop; + let leave = noop; + let iterators = iteratorsNatural; + let walkReducer = (ret, data, item, list) => ret || walkNode(data, item, list); + const context = { + break: breakWalk, + skip: skipNode, + + root, + stylesheet: null, + atrule: null, + atrulePrelude: null, + rule: null, + selector: null, + block: null, + declaration: null, + function: null + }; + + if (typeof options === 'function') { + enter = options; + } else if (options) { + enter = ensureFunction(options.enter); + leave = ensureFunction(options.leave); + + if (options.reverse) { + iterators = iteratorsReverse; + } + + if (options.visit) { + if (fastTraversalIteratorsNatural.hasOwnProperty(options.visit)) { + iterators = options.reverse + ? fastTraversalIteratorsReverse[options.visit] + : fastTraversalIteratorsNatural[options.visit]; + } else if (!types.hasOwnProperty(options.visit)) { + throw new Error('Bad value `' + options.visit + '` for `visit` option (should be: ' + Object.keys(types).sort().join(', ') + ')'); + } + + enter = invokeForType(enter, options.visit); + leave = invokeForType(leave, options.visit); + } + } + + if (enter === noop && leave === noop) { + throw new Error('Neither `enter` nor `leave` walker handler is set or both aren\'t a function'); + } + + walkNode(root); + }; + + walk.break = breakWalk; + walk.skip = skipNode; + + walk.find = function(ast, fn) { + let found = null; + + walk(ast, function(node, item, list) { + if (fn.call(this, node, item, list)) { + found = node; + return breakWalk; + } + }); + + return found; + }; + + walk.findLast = function(ast, fn) { + let found = null; + + walk(ast, { + reverse: true, + enter(node, item, list) { + if (fn.call(this, node, item, list)) { + found = node; + return breakWalk; + } + } + }); + + return found; + }; + + walk.findAll = function(ast, fn) { + const found = []; + + walk(ast, function(node, item, list) { + if (fn.call(this, node, item, list)) { + found.push(node); + } + }); + + return found; + }; + + return walk; +} + +exports.createWalker = createWalker; |
