1313
1414const CACHE = { } ;
1515
16- const TEMPLATE = document . createElement ( 'template' ) ;
17-
18- const reg = / ( \$ _ h \[ \d + \] ) / g;
19-
2016export default function html ( statics ) {
21- const tpl = CACHE [ statics ] || ( CACHE [ statics ] = build ( statics ) ) ;
17+ const str = statics . join ( '\0' ) ;
18+ const tpl = CACHE [ str ] || ( CACHE [ str ] = build ( str ) ) ;
2219 // eslint-disable-next-line prefer-rest-params
2320 return tpl ( this , arguments ) ;
2421}
2522
23+ const TAG_START = 60 ;
24+ const TAG_END = 62 ;
25+ const EQUALS = 61 ;
26+ const QUOTE_DOUBLE = 34 ;
27+ const QUOTE_SINGLE = 39 ;
28+ const TAB = 9 ;
29+ const NEWLINE = 10 ;
30+ const RETURN = 13 ;
31+ const SPACE = 32 ;
32+ const SLASH = 47 ;
33+
34+ const MODE_WHITESPACE = 0 ;
35+ const MODE_TEXT = 1 ;
36+ const MODE_TAGNAME = 9 ;
37+ const MODE_ATTRIBUTE = 13 ;
38+ const MODE_SKIP = 47 ;
39+
2640/** Create a template function given strings from a tagged template. */
27- function build ( statics ) {
28- let str = statics [ 0 ] , i = 1 ;
29- while ( i < statics . length ) {
30- str += '$_h[' + i + ']' + statics [ i ++ ] ;
31- }
32- // Template string preprocessing:
33- // - replace <${Foo}> with <c c@=${Foo}>
34- // - replace <x /> with <x></x>
35- // - replace <${Foo}>a<//>b with <c c@=${Foo}>a</c>b
36- TEMPLATE . innerHTML = str
37- . replace ( / < (?: ( \/ ) \/ | ( \/ ? ) ( \$ _ h \[ \d + \] ) ) / g, '<$1$2c c@=$3' )
38- . replace ( / < ( [ \w : - ] + ) (?: \s [ ^ < > ] * ?) ? ( \/ ? ) > / g, ( str , name , a ) => (
39- str . replace ( / (?: ' .* ?' | " .* ?" | ( [ A - Z ] ) ) / g, ( s , c ) => c ? ':::' + c : s ) + ( a ? '</' + name + '>' : '' )
40- ) )
41- . trim ( ) ;
42- return Function ( 'h' , '$_h' , 'return ' + walk ( ( TEMPLATE . content || TEMPLATE ) . firstChild ) ) ;
43- }
41+ function build ( input ) {
42+ let out = 'return ' ;
43+ let buffer = '' ;
44+ let mode = MODE_WHITESPACE ;
45+ let fieldIndex = 1 ;
46+ let field = '' ;
47+ let hasChildren = 0 ;
48+ let propCount = 0 ;
49+ let spreads = 0 ;
50+ let quote = 0 ;
51+ let spread , slash , charCode , inTag , propName , propHasValue ;
4452
45- /** Traverse a DOM tree and serialize it to hyperscript function calls */
46- function walk ( n ) {
47- if ( n . nodeType != 1 ) {
48- if ( n . nodeType == 3 && n . data ) return field ( n . data , ',' ) ;
49- return 'null' ;
50- }
51- let str = '' ,
52- nodeName = field ( n . localName , str ) ,
53- sub = '' ,
54- start = ',({' ;
55- for ( let i = 0 ; i < n . attributes . length ; i ++ ) {
56- const name = n . attributes [ i ] . name ;
57- const value = n . attributes [ i ] . value ;
58- if ( name == 'c@' ) {
59- nodeName = value ;
53+ function commit ( ) {
54+ if ( ! inTag ) {
55+ if ( field || ( buffer = buffer . trim ( ) ) ) {
56+ // if (field || buffer) {
57+ if ( hasChildren ++ ) out += ',' ;
58+ out += field || JSON . stringify ( buffer ) ;
59+ }
60+ }
61+ else if ( mode === MODE_TAGNAME ) {
62+ if ( hasChildren ++ ) out += ',' ;
63+ out += 'h(' + ( field || JSON . stringify ( buffer ) ) ;
64+ mode = MODE_WHITESPACE ;
6065 }
61- else if ( name . substring ( 0 , 3 ) == '...' ) {
62- sub = '' ;
63- start = ',Object.assign({' ;
64- str += '},' + name . substring ( 3 ) + ',{' ;
66+ else if ( mode === MODE_ATTRIBUTE || ( mode === MODE_WHITESPACE && buffer === '...' ) ) {
67+ // if (!propCount++) {
68+ // propsStart = out.length + 1;
69+ // }
70+ if ( mode === MODE_WHITESPACE ) {
71+ spread = true ;
72+ if ( ! spreads ++ ) {
73+ if ( propCount === 0 ) out += ',Object.assign({},' ;
74+ else out = out . replace ( / , \( \{ ( .* ?) $ / , ',Object.assign({},{$1' ) + '},' ;
75+ // out = out.substring(0, propsStart) + out.substring;
76+ }
77+ // out += ',' + field;
78+ out += field + ',{' ;
79+ propCount ++ ;
80+ }
81+ // out += ',';
82+ else if ( propName ) {
83+ // out += ',' + propName + ':';
84+ if ( ! spread ) out += ',' ;
85+ if ( propCount === 0 ) out += '({' ;
86+ out += propName + ':' ;
87+ out += field || ( ( propHasValue || buffer ) && JSON . stringify ( buffer ) ) || 'true' ;
88+ propName = '' ;
89+ spread = false ;
90+ propCount ++ ;
91+ }
92+ propHasValue = false ;
6593 }
66- else {
67- str += `${ sub } "${ name . replace ( / : : : ( \w ) / g, ( s , i ) => i . toUpperCase ( ) ) } ":${ value ? field ( value , '+' ) : true } ` ;
68- sub = ',' ;
94+ else if ( mode === MODE_WHITESPACE ) {
95+ // if (buffer === '...') {
96+ // spread = true;
97+ // }
98+ // else {
99+ // spread = false;
100+ mode = MODE_ATTRIBUTE ;
101+ // we're in an attribute name
102+ propName = buffer ;
103+ buffer = field = '' ;
104+ commit ( ) ;
105+ mode = MODE_WHITESPACE ;
106+ // }
69107 }
108+ buffer = field = '' ;
109+ // hasChildren++;
70110 }
71- str = 'h(' + nodeName + start + str + '})' ;
72- let child = n . firstChild ;
73- while ( child ) {
74- str += ',' + walk ( child ) ;
75- child = child . nextSibling ;
111+
112+ for ( let i = 0 ; i < input . length ; i ++ ) {
113+ // prevCharCode = charCode;
114+ charCode = input . charCodeAt ( i ) ;
115+ field = '' ;
116+
117+ if ( charCode === QUOTE_SINGLE || charCode === QUOTE_DOUBLE ) {
118+ if ( quote === charCode ) {
119+ quote = 0 ;
120+ // commit();
121+ continue ;
122+ }
123+ if ( quote === 0 ) {
124+ quote = charCode ;
125+ continue ;
126+ }
127+ }
128+
129+ if ( charCode === 0 ) {
130+ // if (mode !== MODE_TAGNAME && mode !== MODE_ATTRIBUTE) commit();
131+ if ( ! inTag ) commit ( ) ;
132+ field = `$_h[${ fieldIndex ++ } ]` ;
133+ commit ( ) ;
134+ continue ;
135+ }
136+
137+ if ( quote === 0 ) {
138+ switch ( charCode ) {
139+ case TAG_START :
140+ if ( ! inTag ) {
141+ // commit buffer
142+ commit ( ) ;
143+ inTag = true ;
144+ propCount = 0 ;
145+ slash = spread = propHasValue = false ;
146+ mode = MODE_TAGNAME ;
147+ //if (buffer = buffer.trim()) out += JSON.stringify(buffer);
148+ // if (hasChildren++) out += ',';
149+ // out += 'h(';
150+ // buffer = '';
151+ continue ;
152+ }
153+
154+ case TAG_END :
155+ if ( inTag ) {
156+ commit ( ) ;
157+ if ( mode !== MODE_SKIP ) {
158+ // if (prevCharCode === SLASH) {
159+ if ( propCount === 0 ) {
160+ out += ',null' ;
161+ }
162+ else {
163+ out += '})' ;
164+ }
165+ }
166+ if ( slash ) {
167+ // tags.pop();
168+ out += ')' ;
169+ }
170+ inTag = false ;
171+ propCount = 0 ;
172+ mode = MODE_TEXT ;
173+ continue ;
174+ }
175+
176+ // case QUOTE_SINGLE:
177+ // case QUOTE_DOUBLE:
178+ // if (quote === charCode) {
179+ // quote = 0;
180+ // }
181+ // if (quote === 0) {
182+ // quote = charCode;
183+ // }
184+ // continue;
185+
186+ case EQUALS :
187+ if ( inTag ) {
188+ mode = MODE_ATTRIBUTE ;
189+ propHasValue = true ;
190+ propName = buffer ;
191+ buffer = '' ;
192+ continue ;
193+ }
194+
195+ case SLASH :
196+ if ( inTag ) {
197+ if ( ! slash ) {
198+ slash = true ;
199+ // </foo>
200+ // console.log(mode === MODE_TAGNAME, field, buffer.trim());
201+ if ( mode === MODE_TAGNAME && ! field && ! buffer . trim ( ) . length ) {
202+ buffer = field = '' ;
203+ mode = MODE_SKIP ;
204+ }
205+ }
206+ continue ;
207+ }
208+ case TAB :
209+ case NEWLINE :
210+ case RETURN :
211+ case SPACE :
212+ // <a disabled>
213+ if ( inTag ) {
214+ commit ( ) ;
215+ continue ;
216+ }
217+ // else if (!buffer.length) continue;
218+ // commit = inTag === true;
219+ // continue;
220+ }
221+ }
222+
223+ buffer += input . charAt ( i ) ;
224+
225+ // // while ((token = TOKENIZER.exec(statics[i])) || ((field=`$_h[${i}]`), (buffer=''), (lastIndex=0), statics[++i])) {
226+ // // if (char==='\\' || !token) continue;
227+ // if (token[3] != null) break;
228+ // if (!token) {
229+ // if (!inTag) {
230+ // if (buffer) out += JSON.stringify(buffer);
231+ // out += field;
232+ // buffer = '';
233+ // field = null;
234+ // }
235+ // continue;
236+ // }
237+ // buffer += token.input.substring(lastIndex, token.index);
238+ // char = token[0];
239+ // if (!inTag) continue;
240+ // if (prev==='\\') out += prev + char;
241+ // else if (token[1] && !inQuote) inQuote = true;
242+ // else if (inQuote) {
243+ // if (inQuote===token[1]) {
244+ // inQuote = false;
245+ // quotedValue = field || JSON.stringify(buffer);
246+ // }
247+ // }
248+ // else if (char==='<') inTag = true;
249+ // else if (char==='>') inTag = false;
250+ // else if (char==='/' && prev==='<') out += ')';
251+ // else if (char==='=') {
252+ // propName = buffer;
253+ // // out += ',' + JSON.stringify(buffer) + ':';
254+ // }
255+ // else if (token[2]) {
256+ // if (prev==='<') {
257+ // const tag = field || JSON.stringify(buffer);
258+ // tags.push(tag);
259+ // if (buffer) {
260+ // out += JSON.stringify(buffer);
261+ // }
262+ // if (hasChildren) out += ',';
263+ // out += `h(${tag}`;
264+ // buffer = '';
265+ // hasChildren = true;
266+ // // childIndex = 0;
267+ // }
268+ // else {
269+ // out += ',' + JSON.stringify(propName || buffer) + ':' + (quotedValue || 'true');
270+ // buffer = propName = quotedValue = '';
271+ // }
272+ // }
273+ // prev = char;
274+ // field = null;
275+ // lastIndex = TOKENIZER.lastIndex;
76276 }
77- return str + ')' ;
277+ commit ( ) ;
278+ return Function ( 'h' , '$_h' , out ) ;
279+ // try {
280+ // return Function('h', '$_h', out);
281+ // }
282+ // catch (e) {
283+ // throw `input: ${out}\n${e}`;
284+ // }
78285}
79286
287+
80288/** Serialize a field to a String or reference for use in generated code. */
81- function field ( value , sep ) {
82- const matches = value . match ( reg ) ;
83- let strValue = JSON . stringify ( value ) ;
84- if ( matches != null ) {
85- if ( matches [ 0 ] === value ) return value ;
86- strValue = strValue . replace ( reg , `"${ sep } $1${ sep } "` ) . replace ( / " [ + , ] " / g, '' ) ;
87- if ( sep == ',' ) strValue = `[${ strValue } ]` ;
88- }
89- return strValue ;
90- }
289+ // function field(value, sep) {
290+ // const matches = value.match(reg);
291+ // let strValue = JSON.stringify(value);
292+ // if (matches != null) {
293+ // if (matches[0] === value) return value;
294+ // strValue = strValue.replace(reg, `"${sep}$1${sep}"`).replace(/"[+,]"/g, '');
295+ // if (sep == ',') strValue = `[${strValue}]`;
296+ // }
297+ // return strValue;
298+ // }
0 commit comments