Skip to content

Commit b4ac1bc

Browse files
authored
Merge pull request #41 from jviide/custom-jsx-parser-03
Don't use endsWith, produce cleaner output, fix text with a lone quote
2 parents 4760c17 + 40cdc09 commit b4ac1bc

File tree

2 files changed

+51
-60
lines changed

2 files changed

+51
-60
lines changed

src/index.mjs

Lines changed: 49 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,14 @@ function build(statics) {
4545
let field = '';
4646
let hasChildren = 0;
4747
let props = '';
48+
let propsClose = '';
49+
let spreadClose = '';
4850
let quote = 0;
49-
let spread, slash, charCode, inTag, propName, propHasValue;
51+
let slash, charCode, inTag, propName, propHasValue;
5052

5153
function commit() {
5254
if (!inTag) {
53-
if (field || (buffer = buffer.replace(/^\s*\n+\s*|\s*\n+\s*$/g,''))) {
55+
if (field || (buffer = buffer.replace(/^\s*\n\s*|\s*\n\s*$/g,''))) {
5456
if (hasChildren++) out += ',';
5557
out += field || JSON.stringify(buffer);
5658
}
@@ -62,19 +64,18 @@ function build(statics) {
6264
}
6365
else if (mode === MODE_ATTRIBUTE || (mode === MODE_WHITESPACE && buffer === '...')) {
6466
if (mode === MODE_WHITESPACE) {
65-
if (!spread) {
66-
spread = true;
67-
if (!props) props = 'Object.assign({},';
68-
else props = 'Object.assign({},' + props + '},';
67+
if (!spreadClose) {
68+
spreadClose = ')';
69+
if (!props) props = 'Object.assign({}';
70+
else props = 'Object.assign({},' + props;
6971
}
70-
else {
71-
props += '},';
72-
}
73-
props += field + ',{';
72+
props += propsClose + ',' + field;
73+
propsClose = '';
7474
}
7575
else if (propName) {
7676
if (!props) props += '{';
77-
else if (!props.endsWith('{')) props += ',';
77+
else props += ',' + (propsClose ? '' : '{');
78+
propsClose = '}';
7879
props += JSON.stringify(propName) + ':';
7980
props += field || ((propHasValue || buffer) && JSON.stringify(buffer)) || 'true';
8081
propName = '';
@@ -99,90 +100,79 @@ function build(statics) {
99100
commit();
100101
}
101102

102-
const input = statics[i];
103-
for (let j=0; j<input.length; j++) {
104-
charCode = input.charCodeAt(j);
105-
field = '';
103+
for (let j=0; j<statics[i].length; j++) {
104+
charCode = statics[i].charCodeAt(j);
106105

107-
if (charCode === QUOTE_SINGLE || charCode === QUOTE_DOUBLE) {
108-
if (quote === charCode) {
109-
quote = 0;
110-
continue;
111-
}
112-
if (quote === 0) {
113-
quote = charCode;
106+
if (!inTag) {
107+
if (charCode === TAG_START) {
108+
// commit buffer
109+
commit();
110+
inTag = true;
111+
spreadClose = propsClose = props = '';
112+
slash = propHasValue = false;
113+
mode = MODE_TAGNAME;
114114
continue;
115115
}
116116
}
117-
118-
if (quote === 0) {
119-
switch (charCode) {
120-
case TAG_START:
121-
if (!inTag) {
122-
// commit buffer
123-
commit();
124-
inTag = true;
125-
props = '';
126-
slash = spread = propHasValue = false;
127-
mode = MODE_TAGNAME;
128-
continue;
129-
}
130-
131-
case TAG_END:
132-
if (inTag) {
117+
else {
118+
if (charCode === QUOTE_SINGLE || charCode === QUOTE_DOUBLE) {
119+
if (quote === charCode) {
120+
quote = 0;
121+
continue;
122+
}
123+
if (quote === 0) {
124+
quote = charCode;
125+
continue;
126+
}
127+
}
128+
129+
if (quote === 0) {
130+
switch (charCode) {
131+
case TAG_END:
133132
commit();
134133
if (mode !== MODE_SKIP) {
135134
if (!props) {
136135
out += ',null';
137136
}
138137
else {
139-
out += ',' + props + '}' + (spread ? ')' : '');
138+
out += ',' + props + propsClose + spreadClose;
140139
}
141140
}
142141
if (slash) {
143142
out += ')';
144143
}
145-
spread = inTag = false;
144+
inTag = false;
146145
props = '';
147146
mode = MODE_TEXT;
148147
continue;
149-
}
150-
151-
case EQUALS:
152-
if (inTag) {
148+
case EQUALS:
153149
mode = MODE_ATTRIBUTE;
154150
propHasValue = true;
155151
propName = buffer;
156152
buffer = '';
157153
continue;
158-
}
159-
160-
case SLASH:
161-
if (inTag) {
154+
case SLASH:
162155
if (!slash) {
163156
slash = true;
164157
// </foo>
165-
if (mode === MODE_TAGNAME && !field && !buffer.trim().length) {
158+
if (mode === MODE_TAGNAME && !buffer.trim()) {
166159
buffer = field = '';
167160
mode = MODE_SKIP;
168161
}
169162
}
170163
continue;
171-
}
172-
case TAB:
173-
case NEWLINE:
174-
case RETURN:
175-
case SPACE:
176-
// <a disabled>
177-
if (inTag) {
164+
case TAB:
165+
case NEWLINE:
166+
case RETURN:
167+
case SPACE:
168+
// <a disabled>
178169
commit();
179170
mode = MODE_WHITESPACE;
180171
continue;
181-
}
172+
}
182173
}
183174
}
184-
185-
buffer += input.charAt(j);
175+
buffer += statics[i].charAt(j);
186176
}
187177
}
188178
commit();

test/index.test.mjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ describe('htm', () => {
8686

8787
test('multiple spread props in one element', () => {
8888
expect(html`<a ...${{ foo: 'bar' }} ...${{ quux: 'baz' }} />`).toEqual({ tag: 'a', props: { foo: 'bar', quux: 'baz' }, children: [] });
89-
});
89+
});
9090

9191
test('mixed spread + static props', () => {
9292
expect(html`<a b ...${{ foo: 'bar' }} />`).toEqual({ tag: 'a', props: { b: true, foo: 'bar' }, children: [] });
@@ -107,6 +107,7 @@ describe('htm', () => {
107107
test('text child', () => {
108108
expect(html`<a>foo</a>`).toEqual({ tag: 'a', props: null, children: ['foo'] });
109109
expect(html`<a>foo bar</a>`).toEqual({ tag: 'a', props: null, children: ['foo bar'] });
110+
expect(html`<a>foo "<b /></a>`).toEqual({ tag: 'a', props: null, children: ['foo "', { tag: 'b', props: null, children: [] }] });
110111
});
111112

112113
test('dynamic child', () => {

0 commit comments

Comments
 (0)