Skip to content

Commit f1e14db

Browse files
committed
updated to simplify and resolve merges
2 parents 999113a + 8c43b4d commit f1e14db

File tree

3 files changed

+202
-1
lines changed

3 files changed

+202
-1
lines changed

lib/parse.js

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*jshint -W030 */
2+
var tagRE = /<[a-zA-Z\-\!\/](?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])*>/g
3+
4+
var parseTag = require('./parse-tag')
5+
// re-used obj for quick lookups of components
6+
var empty = Object.create ? Object.create(null) : {}
7+
8+
module.exports = function parse(html, options) {
9+
options || (options = {})
10+
options.components || (options.components = empty)
11+
var result = []
12+
var current
13+
var level = -1
14+
var arr = []
15+
var byTag = {}
16+
var inComponent = false
17+
18+
if (html.indexOf('<') !== 0) {
19+
var end = html.indexOf('<')
20+
result.push({
21+
type: 'text',
22+
content: end === -1 ? html : html.substring(0, end),
23+
})
24+
}
25+
26+
html.replace(tagRE, function (tag, index) {
27+
if (inComponent) {
28+
if (tag !== '</' + current.name + '>') {
29+
return
30+
} else {
31+
inComponent = false
32+
}
33+
}
34+
var isOpen = tag.charAt(1) !== '/'
35+
var start = index + tag.length
36+
var nextChar = html.charAt(start)
37+
var parent
38+
39+
if (isOpen) {
40+
level++
41+
42+
current = parseTag(tag)
43+
if (current.type === 'tag' && options.components[current.name]) {
44+
current.type = 'component'
45+
inComponent = true
46+
}
47+
48+
if (
49+
!current.voidElement &&
50+
!inComponent &&
51+
nextChar &&
52+
nextChar !== '<'
53+
) {
54+
current.children.push({
55+
type: 'text',
56+
content: html.slice(start, html.indexOf('<', start)),
57+
})
58+
}
59+
60+
byTag[current.tagName] = current
61+
62+
// if we're at root, push new base node
63+
if (level === 0) {
64+
result.push(current)
65+
}
66+
67+
parent = arr[level - 1]
68+
69+
if (parent) {
70+
parent.children.push(current)
71+
}
72+
73+
arr[level] = current
74+
}
75+
76+
if (!isOpen || current.voidElement) {
77+
if (
78+
level > -1 &&
79+
(current.voidElement || current.name === tag.slice(2, tag.indexOf(' ')))
80+
) {
81+
level--
82+
}
83+
84+
if (!inComponent && nextChar !== '<' && nextChar) {
85+
// trailing text node
86+
// if we're at the root, push a base text node. otherwise add as
87+
// a child to the current node.
88+
parent = level === -1 ? result : arr[level].children
89+
90+
// calculate correct end of the content slice in case there's
91+
// no tag after the text node.
92+
var end = html.indexOf('<', start)
93+
var content = html.slice(start, end === -1 ? undefined : end)
94+
// if a node is nothing but whitespace, no need to add it.
95+
if (!/^\s*$/.test(content)) {
96+
parent.push({
97+
type: 'text',
98+
content: content,
99+
})
100+
}
101+
}
102+
103+
current = arr[level]
104+
}
105+
})
106+
107+
return result
108+
}

src/parse.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ export default function parse(html, options) {
1313
let level = -1
1414
let inComponent = false
1515

16+
// handle text at top level
17+
if (html.indexOf('<') !== 0) {
18+
var end = html.indexOf('<')
19+
result.push({
20+
type: 'text',
21+
content: end === -1 ? html : html.substring(0, end),
22+
})
23+
}
24+
1625
html.replace(tagRE, function (tag, index) {
1726
if (inComponent) {
1827
if (tag !== '</' + current.name + '>') {
@@ -76,7 +85,12 @@ export default function parse(html, options) {
7685
}
7786

7887
if (!isOpen || current.voidElement) {
79-
level--
88+
if (
89+
level > -1 &&
90+
(current.voidElement || current.name === tag.slice(2, -1))
91+
) {
92+
level--
93+
}
8094
if (!inComponent && nextChar !== '<' && nextChar) {
8195
// trailing text node
8296
// if we're at the root, push a base text node. otherwise add as

test/parse.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,39 @@ test('parse', function (t) {
504504
'should handle trailing text nodes at the top-level'
505505
)
506506

507+
html = 'Hi <div>There</div>'
508+
parsed = HTML.parse(html)
509+
t.deepEqual(
510+
parsed,
511+
[
512+
{
513+
type: 'text',
514+
content: 'Hi ',
515+
},
516+
{
517+
type: 'tag',
518+
name: 'div',
519+
attrs: {},
520+
voidElement: false,
521+
children: [{ type: 'text', content: 'There' }],
522+
},
523+
],
524+
'should handle leading text nodes at the top-level'
525+
)
526+
527+
html = 'Hi There'
528+
parsed = HTML.parse(html)
529+
t.deepEqual(
530+
parsed,
531+
[
532+
{
533+
type: 'text',
534+
content: 'Hi There',
535+
},
536+
],
537+
'should handle plain strings of text with no tags'
538+
)
539+
507540
html = '<div>Hi</div> There <span>something</span> <a></a>else '
508541
parsed = HTML.parse(html)
509542
t.deepEqual(
@@ -632,6 +665,52 @@ test('parse', function (t) {
632665
'should parse attr values with quotes, opposite'
633666
)
634667

668+
html = '<div>Hi</span>There</div>'
669+
parsed = HTML.parse(html)
670+
t.deepEqual(
671+
parsed,
672+
[
673+
{
674+
type: 'tag',
675+
name: 'div',
676+
attrs: {},
677+
voidElement: false,
678+
children: [
679+
{ type: 'text', content: 'Hi' },
680+
{ type: 'text', content: 'There' },
681+
],
682+
},
683+
],
684+
"should skip over closing tags that don't match the current tag name"
685+
)
686+
687+
html = '<p>Hi There</p></span>root text</p><p>Try again</p>'
688+
parsed = HTML.parse(html)
689+
t.deepEqual(
690+
parsed,
691+
[
692+
{
693+
type: 'tag',
694+
name: 'p',
695+
attrs: {},
696+
voidElement: false,
697+
children: [{ type: 'text', content: 'Hi There' }],
698+
},
699+
{
700+
type: 'text',
701+
content: 'root text',
702+
},
703+
{
704+
type: 'tag',
705+
name: 'p',
706+
attrs: {},
707+
voidElement: false,
708+
children: [{ type: 'text', content: 'Try again' }],
709+
},
710+
],
711+
'should not go lower than the root level (-1)'
712+
)
713+
635714
t.end()
636715
})
637716

0 commit comments

Comments
 (0)