Skip to content

Commit 02862f7

Browse files
authored
Fix boolean attribute hadling s.t. it doesnt have 'undefined' value (#40)
- While there I also updated handling of children/attributes to allow them to be empty. This improves efficiency over the wire.
1 parent b16da43 commit 02862f7

File tree

4 files changed

+75
-20
lines changed

4 files changed

+75
-20
lines changed

src/ast.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,25 @@ export function fromNode(node: Node): NodeProto {
4545
}
4646

4747
const elementNode = node as Element;
48-
const attributes = Array.from(elementNode.attributes).map(
49-
({name, value}) => ({name, value})
50-
);
51-
return {
48+
const protoNode: NodeProto = {
5249
tagid: getTagId(elementNode.tagName),
5350
value: elementNode.tagName.toLowerCase(),
54-
attributes,
55-
children: Array.from(node.childNodes).map(fromNode),
5651
};
52+
53+
if (elementNode.attributes?.length) {
54+
protoNode.attributes = Array.from(elementNode.attributes).map(
55+
({name, value}) => {
56+
if (value === '') {
57+
return {name};
58+
}
59+
return {name, value};
60+
}
61+
);
62+
}
63+
if (node.childNodes?.length) {
64+
protoNode.children = Array.from(node.childNodes).map(fromNode);
65+
}
66+
return protoNode;
5767
}
5868

5969
const termRegex = /[\w-]+/gm;

src/dom.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,19 @@ function fromTreeProtoHelper(nodes: NodeProto[], doc: Document, parent: Node) {
7070
}
7171

7272
const domNode = doc.createElement(node.value);
73-
for (const {name, value} of node.attributes) {
74-
domNode.setAttribute(name, value);
73+
if (node.attributes) {
74+
for (const {name, value} of node.attributes) {
75+
if (typeof value === 'undefined') {
76+
domNode.setAttribute(name, '');
77+
} else {
78+
domNode.setAttribute(name, value);
79+
}
80+
}
7581
}
7682
parent.appendChild(domNode);
7783

78-
fromTreeProtoHelper(node.children, doc, domNode);
84+
if (node.children) {
85+
fromTreeProtoHelper(node.children, doc, domNode);
86+
}
7987
}
8088
}

src/protos.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ export interface TextNodeProto {
1919
export interface ElementNodeProto {
2020
tagid: number;
2121
value: string;
22-
attributes: Array<AttributeProto>;
23-
children: Array<NodeProto>;
22+
attributes?: Array<AttributeProto>;
23+
children?: Array<NodeProto>;
2424
}
2525

2626
export interface AttributeProto {

test/test-index.ts

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@
1414
* limitations under the License.
1515
*/
1616
import test from 'ava';
17-
import {DocumentNodeProto, NodeProto, TreeProto} from '../src/protos.js';
17+
import {
18+
DocumentNodeProto,
19+
ElementNodeProto,
20+
NodeProto,
21+
TreeProto,
22+
} from '../src/protos.js';
1823
import {getTagId} from '../src/htmltagenum.js';
1924
import {renderAstDocument, renderAstNodes} from '../src/index.js';
2025

@@ -26,20 +31,30 @@ function h(
2631
attributes: Object = {},
2732
children: Array<NodeProto | string> = []
2833
): NodeProto {
29-
return {
34+
const nodeProto: NodeProto = {
3035
tagid: getTagId(tagName),
3136
value: tagName,
32-
attributes: Object.entries(attributes).map(([name, value]) => ({
33-
name,
34-
value,
35-
})),
36-
children: children.map((child) => {
37+
};
38+
if (Object.entries(attributes).length) {
39+
nodeProto.attributes = Object.entries(attributes).map(([name, value]) => {
40+
if (value === '') {
41+
return {name};
42+
}
43+
return {
44+
name,
45+
value,
46+
};
47+
});
48+
}
49+
if (children.length) {
50+
nodeProto.children = children.map((child) => {
3751
if (typeof child === 'string') {
3852
return {value: child, num_terms: child.match(/[\w]+/gm)?.length ?? 0};
3953
}
4054
return child;
41-
}),
42-
};
55+
});
56+
}
57+
return nodeProto;
4358
}
4459

4560
/**
@@ -164,6 +179,28 @@ test('should conserve quirks_mode and root', (t) => {
164179
t.deepEqual(renderAstDocument(ast, {}), ast);
165180
});
166181

182+
test('should conserve boolean attributes', (t) => {
183+
const tree: [DocumentNodeProto] = [
184+
{
185+
tagid: 92,
186+
children: [{tagid: 43, value: 'html', attributes: [{name: 'amp'}]}],
187+
},
188+
];
189+
const ast: TreeProto = {root: 42, quirks_mode: true, tree};
190+
t.deepEqual(renderAstDocument(ast, {}), ast);
191+
});
192+
193+
test('should conserve lack of children/attrs for efficiency', (t) => {
194+
const tree: [DocumentNodeProto] = [
195+
{
196+
tagid: 92,
197+
children: [{tagid: 43, value: 'html'}],
198+
},
199+
];
200+
const ast: TreeProto = {root: 42, quirks_mode: true, tree};
201+
t.deepEqual(renderAstDocument(ast, {}), ast);
202+
});
203+
167204
test('should set tagids of element nodes', (t) => {
168205
function buildAmpList(element) {
169206
const b = element.ownerDocument.createElement('b');

0 commit comments

Comments
 (0)