Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add initial tests (WIP) #6

Merged
merged 8 commits into from
Sep 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
node-version: [18.x]
fail-fast: false
steps:
- name: Checkout
Expand Down
247 changes: 170 additions & 77 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,80 +1,173 @@
# parse5-tools
# @parse5/tools

A set of tools for interacting with and manipulating a parse5 AST.

# WIP

This is a work in progress until we achieve the same functionality dom5 and modernweb have.

## Suggested interface

* Tree interaction
* replaceWith
* Attributes
* setAttribute
* getAttribute
* hasAttribute
* removeAttribute
* getAttributeIndex
* getAttributes
* Node interaction
* getTextContent
* setTextContent
* normalizeNode
* Traversal
* query
* queryAll
* treeMap
* ancestors
* previousSiblings
* nextSiblings
* prior
* walk

## dom5

* Mutation
* [x] replaceNode
* [x] removeNodeSaveChildren - use `replaceWith`
* [ ] removeFakeRootElements
* Iteration
* [x] treeMap
* [x] depthFirst
* [x] depthFirstReversed
* [x] depthFirstIncludingTemplates
* [x] ancestors
* [x] previousSiblings
* [x] prior
* [x] query
* [x] queryAll
* Utilities
* [x] getTextContent
* [x] getAttribute
* [x] getAttributeIndex
* [x] hasAttribute
* [x] setAttribute
* [x] removeAttribute
* [x] normalize
* [x] setTextContent

Many of the iteration functions use a predicate concept to allow filtering.

## modernweb

* Mutation
* [ ] createScript - use `createElement` instead
* [x] setAttribute
* [ ] setAttributes - use `setAttribute` instead
* [x] removeAttribute
* [ ] prependToDocument
* [ ] appendToDocument
* Utilities
* [ ] isHtmlFragment
* [x] hasAttribute
* [x] getAttribute
* [x] getAttributes
* Iteration
* [x] findNode
* [x] findNodes
* [ ] findElement - use `findNode` with a type guard
* [ ] findElements - use `findNodes` with a type guard
## Why?

The parse5 tree adapter architecture can make AST types, traversal and
manipulation difficult due to its customisability.

This package introduces some assumptions (i.e. removes some customisability)
in order to provide a more trivial interface to the parse5 AST for the common
use case.

Due to this, the types in various places are also simplified and improved.

## Tools

### Node type guards

A full set of node type guard functions are availabile:

* `isDocument`
* `isDocumentFragment`
* `isTemplateNode`
* `isElementNode`
* `isCommentNode`
* `isDocumentTypeNode`
* `isTextNode`

Each of these consumes a `Node` and acts as a TypeScript type guard:

```ts
if (isDocument(node)) {
// access document-specific properties
}
```

### Parent/child type guards

These help with determining if a given node can have children, or can be
a child.

* `isChildNode`
* `isParentNode`

These too are TypeScript type guards:

```ts
if (isChildNode(node)) {
// interact with node.parentNode
}

if (isParentNode(node)) {
// interact with node.childNodes
}
```

### Child manipulation

If you need to mutate a child:

* `replaceWith(node, ...replacements)` - replaces a given node with one or more
nodes
* `spliceChildren(node, start, deleteCount[, ...children])` - splices the
children of a node just the same as `Array#splice`

### Attributes

For interacting with and mutating attributes of an element:

* `setAttribute(node, name, value)`
* `getAttribute(node, name)`
* `hasAttribute(node, name)`
* `removeAttribute(node, name)`
* `getAttributeIndex(node, name)`

### Text manipulation

For dealing with text content of nodes:

* `getTextContent(node)`
* `setTextContent(node, str)`

### Traversal

Unless otherwise specified, all traversal functions are _depth first_.

Additionally, all capable of returning multiple nodes are iterators.

#### `query(node, condition)`

From a given node, this queries for a child at any depth which matches the
condition.

For example, to find the first document fragment:

```ts
query(
node,
(node) => isDocumentFragment(node)
);
```

#### `queryAll(node[, condition])`

From a given node, this queries for all children at any depth which match
the condition.

For example, to find all elements:

```ts
const elements = query(
node,
(node) => isElementNode(node)
);

for (const element of elements) {
// do something
}
```

#### `ancestors(node)`

Discovers all parents of the specified node until the root document.

#### `walkChildren(node)`

Discovers all children of the specified node, depth-first.

#### `previousSiblings(node)`

Discovers all previous siblings of the specified node.

#### `nextSiblings(node)`

Discovers all next siblings of the specified node.

#### `traverse`

The traverse function allows you to specify a visitor which will be called
for each matching type encountered while traversing the tree depth-first.

For example:

```ts
traverse(node, {
text: (textNode) => {
// do something with a text node
}
});
```

Each node type can have a visitor (e.g. you could have an `element` function).

##### `pre:node`

There is one special visit function: `pre:node`.

This is called before visiting any node and will prevent traversing into
the current node's children if it returns false.

For example:

```ts
traverse(node, {
'pre:node': (node) => {
return isElement(node);
}
});
```

This example would traverse into the children of only element nodes as all
others would have returned false.