Skip to content

Commit d9579c5

Browse files
Update: Reorganize code to be more efficient
1 parent 1ca9d17 commit d9579c5

File tree

7 files changed

+120
-292
lines changed

7 files changed

+120
-292
lines changed

src/ast.ts

Lines changed: 0 additions & 77 deletions
This file was deleted.

src/dom.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
export enum NodeType {
2+
ELEMENT_NODE = 1,
3+
TEXT_NODE = 3,
4+
PROCESSING_INSTRUCTION_NODE = 7,
5+
COMMENT_NODE = 8,
6+
DOCUMENT_NODE = 9,
7+
DOCUMENT_TYPE_NODE = 10,
8+
DOCUMENT_FRAGMENT_NODE = 11,
9+
}
10+
11+
export function getAttributes(elementAttributes: NamedNodeMap) {
12+
const attributes: { [keyof: string]: string } = {}
13+
for (let i = 0; i < elementAttributes.length; i++) {
14+
const attribute = elementAttributes.item(i)
15+
attributes[attribute.nodeName] = attribute.nodeValue
16+
}
17+
return attributes
18+
}
19+
20+
export function parse(html: string): NodeListOf<Node & ChildNode> {
21+
const parser = new DOMParser()
22+
const document = parser.parseFromString(`<htmldomtoreactroot>${html}</htmldomtoreactroot>`, 'application/xml')
23+
return document.childNodes.item(0).childNodes
24+
}

src/index.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import getAst from './ast'
2-
import { renderElements } from './react'
1+
import * as dom from './dom'
2+
import { render } from './react'
33

44
export function parse(html: string): React.ReactNode[] {
5-
const ast = getAst(html)
6-
return renderElements(ast)
5+
if (typeof html !== 'string') {
6+
throw new TypeError('First argument must be a string.')
7+
}
8+
9+
return render(dom.parse(html))
710
}

src/react.ts

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import * as React from 'react'
2-
import { ASTElement, Attributes, NodeType } from './ast'
2+
import { getAttributes, NodeType } from './dom'
3+
4+
export interface Attributes { [keyof: string]: string }
35

46
const reactAttributesMap: Attributes = {
57
acceptcharset: 'acceptCharset',
@@ -49,12 +51,10 @@ const reactAttributesMap: Attributes = {
4951
usemap: 'useMap',
5052
}
5153

52-
function transformAttributes(attributes?: Attributes): Attributes {
53-
if (!attributes) {
54-
return null
55-
}
56-
54+
function transformAttributes(attributesMap: NamedNodeMap): Attributes {
55+
const attributes = getAttributes(attributesMap)
5756
const transformedAttributes: Attributes = {}
57+
5858
Object.keys(attributes).forEach((key) => {
5959
if (!key.startsWith('on')) {
6060
if (reactAttributesMap[key]) {
@@ -67,38 +67,40 @@ function transformAttributes(attributes?: Attributes): Attributes {
6767
return transformedAttributes
6868
}
6969

70-
function transformChildren(children?: ASTElement[]) {
71-
return children || []
70+
function renderTextNode(node: Node & ChildNode) {
71+
return node.nodeValue
7272
}
7373

74-
export function transform(element: ASTElement) {
75-
return {
76-
...element,
77-
attributes: transformAttributes(element.attributes),
78-
children: transformChildren(element.children),
74+
function renderElementNode(node: Node & ChildNode) {
75+
const element = transform(node as Element)
76+
77+
if (element.childNodes) {
78+
return React.createElement(element.nodeName, element.attributes, render(element.childNodes))
7979
}
80+
return React.createElement(element.nodeName, element.attributes)
8081
}
8182

82-
export function renderElement(element: ASTElement): React.ReactNode {
83-
if (element.type === NodeType.TEXT_NODE) {
84-
return element.value
85-
} else if (element.type === NodeType.ELEMENT_NODE) {
86-
const children = element.value ? [
87-
element.value,
88-
...renderElements(element.children),
89-
] : renderElements(element.children)
90-
return React.createElement(element.name, element.attributes, children)
83+
function transform(element: Element) {
84+
return {
85+
attributes: transformAttributes(element.attributes),
86+
childNodes: element.childNodes,
87+
nodeName: element.nodeName,
88+
nodeType: element.nodeType,
89+
nodeValue: element.nodeValue,
9190
}
92-
93-
return null
9491
}
9592

96-
export function renderElements(ast: ASTElement[]): React.ReactNode[] {
97-
return ast.reduce((elements, element) => {
98-
// Only keep text and tags. Remove everything else
99-
if (element.type === NodeType.ELEMENT_NODE || element.type === NodeType.TEXT_NODE) {
100-
elements.push(renderElement(transform(element)))
93+
export function render(nodes: NodeListOf<Node & ChildNode>): React.ReactNode[] {
94+
const elements = []
95+
for (let i = 0; i < nodes.length; i++) {
96+
const node = nodes.item(i)
97+
98+
if (node.nodeType === NodeType.TEXT_NODE) {
99+
elements.push(renderTextNode(node))
100+
} else if (node.nodeType === NodeType.ELEMENT_NODE) {
101+
elements.push(renderElementNode(node))
101102
}
102-
return elements
103-
}, [])
103+
}
104+
105+
return elements
104106
}

tests/api.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ describe('Public API', () => {
99
})
1010

1111
describe('#parse', () => {
12+
it('should throw an error if the first parameter is not a string', (done) => {
13+
try {
14+
htmldomToReact.parse(null)
15+
} catch (err) {
16+
done()
17+
}
18+
})
19+
1220
it('should convert a string to an array of react nodes', () => {
1321
const elements = htmldomToReact.parse('<em key="1"><b key="2">It\' is working</b></em>')
1422
const wrapper = render(React.createElement('div', null, elements))

tests/ast.spec.ts

Lines changed: 0 additions & 56 deletions
This file was deleted.

0 commit comments

Comments
 (0)