Comparing version 0.0.4 to 1.0.0
45
index.js
@@ -10,15 +10,28 @@ 'use strict'; | ||
options = options || {includeDependencies: false}; | ||
function replace(replacement) { | ||
var parents = options.parents = options.parents || []; | ||
var replace = function replace(replacement) { | ||
if (Array.isArray(replacement) && !replace.arrayAllowed) { | ||
throw new Error('replace() can only be called with an array if the last parent is a Block or NamedBlock'); | ||
} | ||
ast = replacement; | ||
}; | ||
replace.arrayAllowed = parents[0] && /^(Named)?Block$/.test(parents[0].type); | ||
if (before) { | ||
var result = before(ast, replace); | ||
if (result === false) { | ||
return ast; | ||
} else if (Array.isArray(ast)) { | ||
// return right here to skip after() call on array | ||
return walkAndMergeNodes(ast); | ||
} | ||
} | ||
var result = before && before(ast, replace); | ||
if (before && result === false) { | ||
return ast; | ||
} | ||
parents.unshift(ast); | ||
switch (ast.type) { | ||
case 'NamedBlock': | ||
case 'Block': | ||
ast.nodes = ast.nodes.map(function (node) { | ||
return walkAST(node, before, after, options); | ||
}); | ||
ast.nodes = walkAndMergeNodes(ast.nodes); | ||
break; | ||
@@ -78,4 +91,18 @@ case 'Case': | ||
} | ||
parents.shift(); | ||
after && after(ast, replace); | ||
return ast; | ||
}; | ||
function walkAndMergeNodes(nodes) { | ||
return nodes.reduce(function (nodes, node) { | ||
var result = walkAST(node, before, after, options); | ||
if (Array.isArray(result)) { | ||
return nodes.concat(result); | ||
} else { | ||
return nodes.concat([result]); | ||
} | ||
}, []); | ||
} | ||
} |
{ | ||
"name": "pug-walk", | ||
"version": "0.0.4", | ||
"version": "1.0.0", | ||
"description": "Walk and transform a pug AST", | ||
@@ -11,9 +11,13 @@ "keywords": [ | ||
"istanbul": "*", | ||
"pug-lexer": "^1.0.0", | ||
"pug-parser": "^1.0.0" | ||
"pug-lexer": "^2.1.0", | ||
"pug-parser": "^2.0.1", | ||
"testit": "^2.1.2" | ||
}, | ||
"scripts": { | ||
"test": "node test", | ||
"coverage": "istanbul cover test.js" | ||
"coverage": "istanbul cover test" | ||
}, | ||
"files": [ | ||
"index.js" | ||
], | ||
"repository": { | ||
@@ -20,0 +24,0 @@ "type": "git", |
123
README.md
# pug-walk | ||
Walk and transform a pug AST | ||
Walk and transform a Pug AST | ||
@@ -16,20 +16,119 @@ [![Build Status](https://img.shields.io/travis/pugjs/pug-walk/master.svg)](https://travis-ci.org/pugjs/pug-walk) | ||
```js | ||
var walk = require('pug-walk'); | ||
``` | ||
### `walk(ast, before, after, options)` | ||
Traverse and optionally transform a Pug AST. | ||
`ast` is not cloned, so any changes done to it will be done directly on the AST provided. | ||
`before` and `after` are functions with the signature `(node, replace)`. `before` is called when a node is first seen, while `after` is called after the children of the node (if any) have already been traversed. | ||
The `replace` parameter is a callback function that can be used to replace the node in the AST. `replace` can also be used to remove this node entirely or add new nodes, by calling `replace` with an array of nodes. This is only possible when the parent node is a Block, as indicated by the property `replace.arrayAllowed`. If it is not possible, and still `replace` is called with an array, `replace` will throw an error. | ||
If `before` returns `false`, the children of this node will not be traversed and left unchanged (unless `replace` has been called). Otherwise, the returned value of `before` is ignored. The returned value of `after` is always ignored. | ||
`options` can contain the following properties: | ||
* `includeDependencies` (boolean): Walk the AST of a loaded dependent file (i.e., includes and extends). Defaults to `false`. | ||
* `parents` (array<ASTNode>): Nodes that are ancestors to the current `ast`. This option is used mainly internally, and users usually do not have to specify it. Defaults to `[]`. | ||
```js | ||
var lex = require('pug-lexer'); | ||
var parse = require('pug-parser'); | ||
var walk = require('pug-walk'); | ||
var ast = walk(parse(lex('.my-class foo')), function before(node, replace) { | ||
// called before walking the children of `node` | ||
// to replace the node, call `replace(newNode)` | ||
// return `false` to skip descending | ||
// Changing content of all Text nodes | ||
// ================================== | ||
var source = '.my-class foo'; | ||
var dest = '.my-class bar'; | ||
var ast = parse(lex(source)); | ||
walk(ast, function before(node, replace) { | ||
if (node.type === 'Text') { | ||
replace({ type: 'Text', val: 'bar', line: node.line }); | ||
node.val = 'bar'; | ||
// Alternatively, you can replace the entire node | ||
// rather than just the text. | ||
// replace({ type: 'Text', val: 'bar', line: node.line }); | ||
} | ||
}, function after(node, replace) { | ||
// called after walking the children of `node` | ||
// to replace the node, call `replace(newNode)` | ||
}, {includeDependencies: true}); | ||
assert.deepEqual(parse(lex('.my-class bar')), ast); | ||
}, { | ||
includeDependencies: true | ||
}); | ||
assert.deepEqual(parse(lex(dest)), ast); | ||
// Convert all simple <strong> elements to text | ||
// ============================================ | ||
var source = 'p abc #[strong NO]\nstrong on its own line'; | ||
var dest = 'p abc #[| NO]\n| on its own line'; | ||
var ast = parse(lex(source)); | ||
walk(ast, function before(node, replace) { | ||
// Find all <strong> tags | ||
if (node.type === 'Tag' && node.name === 'strong') { | ||
var children = node.block.nodes; | ||
// Make sure that the Tag only has one child -- the text | ||
if (children.length === 1 && children[0].type === 'Text') { | ||
// Replace the Tag with the Text | ||
replace({ type: 'Text', val: children[0].val, line: node.line }); | ||
} | ||
} | ||
}, { | ||
includeDependencies: true | ||
}); | ||
assert.deepEqual(parse(lex(dest)), ast); | ||
// Flatten blocks | ||
// ============== | ||
var ast = { | ||
type: 'Block', | ||
nodes: [ | ||
{ type: 'Text', val: 'a' }, | ||
{ | ||
type: 'Block', | ||
nodes: [ | ||
{ type: 'Text', val: 'b' }, | ||
{ | ||
type: 'Block', | ||
nodes: [ { type: 'Text', val: 'c' } ] | ||
}, | ||
{ type: 'Text', val: 'd' } | ||
] | ||
}, | ||
{ type: 'Text', val: 'e' } | ||
] | ||
}; | ||
var dest = { | ||
type: 'Block', | ||
nodes: [ | ||
{ type: 'Text', val: 'a' }, | ||
{ type: 'Text', val: 'b' }, | ||
{ type: 'Text', val: 'c' }, | ||
{ type: 'Text', val: 'd' }, | ||
{ type: 'Text', val: 'e' } | ||
] | ||
}; | ||
// We need to use `after` handler instead of `before` | ||
// handler because we want to flatten the innermost | ||
// blocks first before proceeding onto outer blocks. | ||
walk(ast, null, function after(node, replace) { | ||
if (node.type === 'Block' && replace.arrayAllowed) { | ||
// Replace the block with its contents | ||
replace(node.nodes); | ||
} | ||
}); | ||
assert.deepEqual(dest, ast); | ||
``` | ||
@@ -36,0 +135,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1
138
1
8727
4
5
98