~repos /atoms-element
git clone https://pyrossh.dev/repos/atoms-element.git
A simple web component library for defining your custom elements. It works on both client and server.
20e01ad9
—
Peter John 4 years ago
improve render function
- package.json +6 -0
- readme.md +4 -4
- src/index.js +152 -123
- src/lit-html-server.js +37 -632
- src/lit-html.js +8 -8
- test/index.test.js +32 -18
package.json
CHANGED
|
@@ -29,5 +29,11 @@
|
|
|
29
29
|
},
|
|
30
30
|
"jest": {
|
|
31
31
|
"transform": {}
|
|
32
|
+
},
|
|
33
|
+
"prettier": {
|
|
34
|
+
"printWidth": 160,
|
|
35
|
+
"singleQuote": true,
|
|
36
|
+
"arrowParens": "always",
|
|
37
|
+
"trailingComma": "all"
|
|
32
38
|
}
|
|
33
39
|
}
|
readme.md
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|

|
|
5
5
|
|
|
6
6
|
A simple web component library for defining your custom elements. It works on both client and server. It supports hooks and follows the same
|
|
7
|
-
principles of react.
|
|
7
|
+
principles of react. Data props are attributes on the custom element by default so its easier to debug and functions/handlers are
|
|
8
|
+
methods attached to the element.
|
|
8
9
|
|
|
9
10
|
It uses slightly modified versions of these libraries,
|
|
10
11
|
|
|
11
|
-
1. [lit-html](https://github.com/lit/lit)
|
|
12
|
+
1. [lit-html](https://github.com/lit/lit)
|
|
12
|
-
2. [lit-html-server](https://github.com/popeindustries/lit-html-server) on the server
|
|
13
|
-
|
|
13
|
+
2. [fuco](https://github.com/wtnbass/fuco)
|
|
14
14
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
src/index.js
CHANGED
|
@@ -1,100 +1,124 @@
|
|
|
1
|
-
import { html
|
|
1
|
+
import { html, render as litRender, directive, NodePart, AttributePart, PropertyPart, isPrimitive } from './lit-html.js';
|
|
2
|
-
import { html as litServerHtml, directive as litServerDirective, isNodePart, isAttributePart, unsafePrefixString, renderToString } from './lit-html-server.js';
|
|
3
2
|
|
|
4
3
|
const registry = {};
|
|
5
4
|
const isBrowser = typeof window !== 'undefined';
|
|
6
|
-
export
|
|
5
|
+
export { html, isBrowser };
|
|
6
|
+
|
|
7
|
-
export const render = isBrowser
|
|
7
|
+
export const render = isBrowser
|
|
8
|
+
? litRender
|
|
9
|
+
: (template) => {
|
|
10
|
+
let js = '';
|
|
11
|
+
template.strings.forEach((text, i) => {
|
|
12
|
+
const value = template.values[i];
|
|
8
|
-
|
|
13
|
+
// TODO: remove @click @mouseleave= .handleClick props
|
|
14
|
+
// either here or in lit-html
|
|
15
|
+
// console.log('text', text);
|
|
16
|
+
const type = typeof value;
|
|
17
|
+
js += text;
|
|
18
|
+
if (value === null || !(type === 'object' || type === 'function' || type === 'undefined')) {
|
|
19
|
+
js += type !== 'string' ? String(value) : value;
|
|
20
|
+
} else if (Array.isArray(value)) {
|
|
21
|
+
// Array of TemplateResult
|
|
22
|
+
value.forEach((v) => {
|
|
23
|
+
js += render(v);
|
|
24
|
+
});
|
|
25
|
+
} else if (type === 'object') {
|
|
26
|
+
// TemplateResult
|
|
27
|
+
if (value.strings && value.type === 'html') {
|
|
28
|
+
js += render(value);
|
|
29
|
+
} else {
|
|
30
|
+
js += JSON.stringify(value).replace(/"/g, `'`);
|
|
31
|
+
}
|
|
32
|
+
} else if (type == 'function') {
|
|
33
|
+
js += value();
|
|
34
|
+
} else if (type !== 'undefined') {
|
|
35
|
+
js += value.toString();
|
|
36
|
+
} else {
|
|
37
|
+
// console.log('value', value);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
return js;
|
|
41
|
+
};
|
|
9
42
|
|
|
10
43
|
const previousValues = new WeakMap();
|
|
44
|
+
export const unsafeHTML = isBrowser
|
|
11
|
-
|
|
45
|
+
? directive((value) => (part) => {
|
|
12
|
-
if (isBrowser) {
|
|
13
|
-
|
|
46
|
+
if (!(part instanceof NodePart)) {
|
|
14
|
-
|
|
47
|
+
throw new Error('unsafeHTML can only be used in text bindings');
|
|
15
|
-
|
|
48
|
+
}
|
|
16
|
-
|
|
49
|
+
const previousValue = previousValues.get(part);
|
|
17
|
-
|
|
50
|
+
if (previousValue !== undefined && isPrimitive(value) && value === previousValue.value && part.value === previousValue.fragment) {
|
|
18
|
-
|
|
51
|
+
return;
|
|
19
|
-
|
|
52
|
+
}
|
|
20
|
-
|
|
53
|
+
const template = document.createElement('template');
|
|
21
|
-
|
|
54
|
+
template.innerHTML = value; // innerHTML casts to string internally
|
|
22
|
-
|
|
55
|
+
const fragment = document.importNode(template.content, true);
|
|
23
|
-
|
|
56
|
+
part.setValue(fragment);
|
|
24
|
-
|
|
57
|
+
previousValues.set(part, { value, fragment });
|
|
25
|
-
} else {
|
|
26
|
-
if (!isNodePart(part)) {
|
|
27
|
-
throw Error('The `unsafeHTML` directive can only be used in text nodes');
|
|
28
|
-
}
|
|
29
|
-
part.setValue(`${unsafePrefixString}${value}`);
|
|
30
|
-
}
|
|
31
|
-
})
|
|
58
|
+
})
|
|
59
|
+
: (value) => value;
|
|
32
60
|
|
|
33
61
|
const previousClassesCache = new WeakMap();
|
|
62
|
+
export const classMap = isBrowser
|
|
34
|
-
|
|
63
|
+
? directive((classInfo) => (part) => {
|
|
35
|
-
if (isBrowser) {
|
|
36
|
-
|
|
64
|
+
if (!(part instanceof AttributePart) || part instanceof PropertyPart || part.committer.name !== 'class' || part.committer.parts.length > 1) {
|
|
37
|
-
|
|
65
|
+
throw new Error('The `classMap` directive must be used in the `class` attribute ' + 'and must be the only part in the attribute.');
|
|
38
|
-
}
|
|
39
|
-
const { committer } = part;
|
|
40
|
-
const { element } = committer;
|
|
41
|
-
let previousClasses = previousClassesCache.get(part);
|
|
42
|
-
if (previousClasses === undefined) {
|
|
43
|
-
// Write static classes once
|
|
44
|
-
// Use setAttribute() because className isn't a string on SVG elements
|
|
45
|
-
element.setAttribute('class', committer.strings.join(' '));
|
|
46
|
-
previousClassesCache.set(part, (previousClasses = new Set()));
|
|
47
|
-
}
|
|
48
|
-
const classList = element.classList;
|
|
49
|
-
// Remove old classes that no longer apply
|
|
50
|
-
// We use forEach() instead of for-of so that re don't require down-level
|
|
51
|
-
// iteration.
|
|
52
|
-
previousClasses.forEach(name => {
|
|
53
|
-
if (!(name in classInfo)) {
|
|
54
|
-
classList.remove(name);
|
|
55
|
-
previousClasses.delete(name);
|
|
56
66
|
}
|
|
67
|
+
const { committer } = part;
|
|
68
|
+
const { element } = committer;
|
|
69
|
+
let previousClasses = previousClassesCache.get(part);
|
|
70
|
+
if (previousClasses === undefined) {
|
|
71
|
+
// Write static classes once
|
|
72
|
+
// Use setAttribute() because className isn't a string on SVG elements
|
|
73
|
+
element.setAttribute('class', committer.strings.join(' '));
|
|
74
|
+
previousClassesCache.set(part, (previousClasses = new Set()));
|
|
57
|
-
|
|
75
|
+
}
|
|
76
|
+
const classList = element.classList;
|
|
58
|
-
|
|
77
|
+
// Remove old classes that no longer apply
|
|
78
|
+
// We use forEach() instead of for-of so that re don't require down-level
|
|
79
|
+
// iteration.
|
|
80
|
+
previousClasses.forEach((name) => {
|
|
59
|
-
|
|
81
|
+
if (!(name in classInfo)) {
|
|
60
|
-
const value = classInfo[name];
|
|
61
|
-
if (value != previousClasses.has(name)) {
|
|
62
|
-
// We explicitly want a loose truthy check of `value` because it seems
|
|
63
|
-
// more convenient that '' and 0 are skipped.
|
|
64
|
-
if (value) {
|
|
65
|
-
classList.add(name);
|
|
66
|
-
previousClasses.add(name);
|
|
67
|
-
} else {
|
|
68
82
|
classList.remove(name);
|
|
69
83
|
previousClasses.delete(name);
|
|
70
84
|
}
|
|
85
|
+
});
|
|
86
|
+
// Add or remove classes based on their classMap value
|
|
87
|
+
for (const name in classInfo) {
|
|
88
|
+
const value = classInfo[name];
|
|
89
|
+
if (value != previousClasses.has(name)) {
|
|
90
|
+
// We explicitly want a loose truthy check of `value` because it seems
|
|
91
|
+
// more convenient that '' and 0 are skipped.
|
|
92
|
+
if (value) {
|
|
93
|
+
classList.add(name);
|
|
94
|
+
previousClasses.add(name);
|
|
95
|
+
} else {
|
|
96
|
+
classList.remove(name);
|
|
97
|
+
previousClasses.delete(name);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
71
100
|
}
|
|
72
|
-
}
|
|
73
|
-
|
|
101
|
+
if (typeof classList.commit === 'function') {
|
|
74
|
-
|
|
102
|
+
classList.commit();
|
|
75
|
-
}
|
|
76
|
-
} else {
|
|
77
|
-
if (!isAttributePart(part) || part.name !== 'class') {
|
|
78
|
-
throw Error('The `classMap` directive can only be used in the `class` attribute');
|
|
79
|
-
}
|
|
80
|
-
const classes = classInfo;
|
|
81
|
-
let value = '';
|
|
82
|
-
for (const key in classes) {
|
|
83
|
-
if (classes[key]) {
|
|
84
|
-
value += `${value.length ? ' ' : ''}${key}`;
|
|
85
103
|
}
|
|
104
|
+
})
|
|
105
|
+
: (classes) => {
|
|
106
|
+
let value = '';
|
|
107
|
+
for (const key in classes) {
|
|
108
|
+
if (classes[key]) {
|
|
109
|
+
value += `${value.length ? ' ' : ''}${key}`;
|
|
86
|
-
|
|
110
|
+
}
|
|
87
|
-
part.setValue(value);
|
|
88
|
-
|
|
111
|
+
}
|
|
112
|
+
return value;
|
|
89
|
-
}
|
|
113
|
+
};
|
|
90
114
|
|
|
91
115
|
let currentCursor;
|
|
92
116
|
let currentComponent;
|
|
93
|
-
let logError = msg => {
|
|
117
|
+
let logError = (msg) => {
|
|
94
118
|
console.warn(msg);
|
|
95
119
|
};
|
|
96
120
|
|
|
97
|
-
export const setLogError = fn => {
|
|
121
|
+
export const setLogError = (fn) => {
|
|
98
122
|
logError = fn;
|
|
99
123
|
};
|
|
100
124
|
|
|
@@ -104,10 +128,10 @@ const checkRequired = (context, data) => {
|
|
|
104
128
|
}
|
|
105
129
|
};
|
|
106
130
|
|
|
107
|
-
const checkPrimitive = primitiveType => {
|
|
131
|
+
const checkPrimitive = (primitiveType) => {
|
|
108
132
|
const common = {
|
|
109
133
|
type: primitiveType,
|
|
110
|
-
parse: attr => attr,
|
|
134
|
+
parse: (attr) => attr,
|
|
111
135
|
};
|
|
112
136
|
const validate = (context, data) => {
|
|
113
137
|
if (data === null || typeof data === 'undefined') {
|
|
@@ -134,9 +158,9 @@ const checkPrimitive = primitiveType => {
|
|
|
134
158
|
const checkComplex = (complexType, validate) => {
|
|
135
159
|
const common = {
|
|
136
160
|
type: complexType,
|
|
137
|
-
parse: attr => (attr ? JSON.parse(attr.replace(/'/g, `"`)) : null),
|
|
161
|
+
parse: (attr) => (attr ? JSON.parse(attr.replace(/'/g, `"`)) : null),
|
|
138
162
|
};
|
|
139
|
-
return innerType => {
|
|
163
|
+
return (innerType) => {
|
|
140
164
|
return {
|
|
141
165
|
...common,
|
|
142
166
|
validate: (context, data) => {
|
|
@@ -180,7 +204,7 @@ export const array = checkComplex('array', (innerType, context, data) => {
|
|
|
180
204
|
});
|
|
181
205
|
export const func = checkComplex('function', (innerType, context, data) => {});
|
|
182
206
|
|
|
183
|
-
export const hooks = config => {
|
|
207
|
+
export const hooks = (config) => {
|
|
184
208
|
const h = currentComponent.hooks;
|
|
185
209
|
const c = currentComponent;
|
|
186
210
|
const index = currentCursor++;
|
|
@@ -193,20 +217,20 @@ export const hooks = config => {
|
|
|
193
217
|
return h.values[index];
|
|
194
218
|
};
|
|
195
219
|
|
|
196
|
-
export const __setCurrent__ = c => {
|
|
220
|
+
export const __setCurrent__ = (c) => {
|
|
197
221
|
currentComponent = c;
|
|
198
222
|
currentCursor = 0;
|
|
199
223
|
};
|
|
200
224
|
|
|
201
|
-
export const useDispatchEvent = name =>
|
|
225
|
+
export const useDispatchEvent = (name) =>
|
|
202
226
|
hooks({
|
|
203
|
-
oncreate: (_, c) => data => c.dispatchEvent(new CustomEvent(name, data)),
|
|
227
|
+
oncreate: (_, c) => (data) => c.dispatchEvent(new CustomEvent(name, data)),
|
|
204
228
|
});
|
|
205
|
-
export const useRef = initialValue =>
|
|
229
|
+
export const useRef = (initialValue) =>
|
|
206
230
|
hooks({
|
|
207
231
|
oncreate: (_h, _c) => ({ current: initialValue }),
|
|
208
232
|
});
|
|
209
|
-
export const useState = initialState =>
|
|
233
|
+
export const useState = (initialState) =>
|
|
210
234
|
hooks({
|
|
211
235
|
oncreate: (h, c, i) => [
|
|
212
236
|
typeof initialState === 'function' ? initialState() : initialState,
|
|
@@ -287,14 +311,14 @@ const batch = (runner, pick, callback) => {
|
|
|
287
311
|
while ((p = pick(q))) callback(p);
|
|
288
312
|
};
|
|
289
313
|
const run = runner(flush);
|
|
290
|
-
return c => q.push(c) === 1 && run();
|
|
314
|
+
return (c) => q.push(c) === 1 && run();
|
|
291
315
|
};
|
|
292
|
-
const fifo = q => q.shift();
|
|
316
|
+
const fifo = (q) => q.shift();
|
|
293
|
-
const filo = q => q.pop();
|
|
317
|
+
const filo = (q) => q.pop();
|
|
294
|
-
const microtask = flush => {
|
|
318
|
+
const microtask = (flush) => {
|
|
295
319
|
return () => queueMicrotask(flush);
|
|
296
320
|
};
|
|
297
|
-
const task = flush => {
|
|
321
|
+
const task = (flush) => {
|
|
298
322
|
if (isBrowser) {
|
|
299
323
|
const ch = new window.MessageChannel();
|
|
300
324
|
ch.port1.onmessage = flush;
|
|
@@ -303,9 +327,9 @@ const task = flush => {
|
|
|
303
327
|
return () => setImmediate(flush);
|
|
304
328
|
}
|
|
305
329
|
};
|
|
306
|
-
const enqueueLayoutEffects = batch(microtask, filo, c => c._flushEffects('layoutEffects'));
|
|
330
|
+
const enqueueLayoutEffects = batch(microtask, filo, (c) => c._flushEffects('layoutEffects'));
|
|
307
|
-
const enqueueEffects = batch(task, filo, c => c._flushEffects('effects'));
|
|
331
|
+
const enqueueEffects = batch(task, filo, (c) => c._flushEffects('effects'));
|
|
308
|
-
const enqueueUpdate = batch(microtask, fifo, c => c._performUpdate());
|
|
332
|
+
const enqueueUpdate = batch(microtask, fifo, (c) => c._performUpdate());
|
|
309
333
|
|
|
310
334
|
const BaseElement = isBrowser ? window.HTMLElement : class {};
|
|
311
335
|
|
|
@@ -392,7 +416,7 @@ export class AtomsElement extends BaseElement {
|
|
|
392
416
|
|
|
393
417
|
render() {
|
|
394
418
|
if (isBrowser) {
|
|
395
|
-
this.funcKeys.forEach(key => {
|
|
419
|
+
this.funcKeys.forEach((key) => {
|
|
396
420
|
this.props[key] = this[key];
|
|
397
421
|
});
|
|
398
422
|
render(this.renderer(this.props), this);
|
|
@@ -402,41 +426,46 @@ export class AtomsElement extends BaseElement {
|
|
|
402
426
|
}
|
|
403
427
|
}
|
|
404
428
|
}
|
|
405
|
-
export const getElement = name => registry[name];
|
|
429
|
+
export const getElement = (name) => registry[name];
|
|
406
430
|
|
|
407
431
|
export function defineElement(name, fn, attrTypes = {}) {
|
|
408
432
|
const keys = Object.keys(attrTypes);
|
|
433
|
+
registry[name] = {
|
|
434
|
+
fn,
|
|
435
|
+
attrTypes,
|
|
409
|
-
|
|
436
|
+
clazz: class extends AtomsElement {
|
|
410
|
-
|
|
437
|
+
constructor(attrs) {
|
|
411
|
-
|
|
438
|
+
super();
|
|
412
|
-
|
|
439
|
+
this.name = name;
|
|
413
|
-
|
|
440
|
+
this.renderer = fn;
|
|
414
|
-
|
|
441
|
+
this.attrTypes = attrTypes;
|
|
415
|
-
|
|
442
|
+
this.funcKeys = keys.filter((key) => attrTypes[key].type === 'function');
|
|
416
|
-
|
|
443
|
+
this.attrTypesMap = keys
|
|
417
|
-
|
|
444
|
+
.filter((key) => attrTypes[key].type !== 'function')
|
|
418
|
-
|
|
445
|
+
.reduce((acc, key) => {
|
|
419
|
-
|
|
446
|
+
acc[key.toLowerCase()] = {
|
|
420
|
-
|
|
447
|
+
propName: key,
|
|
421
|
-
|
|
448
|
+
propType: attrTypes[key],
|
|
422
|
-
|
|
449
|
+
};
|
|
423
|
-
|
|
450
|
+
return acc;
|
|
424
|
-
|
|
451
|
+
}, {});
|
|
425
|
-
|
|
452
|
+
if (attrs) {
|
|
426
|
-
|
|
453
|
+
attrs.forEach((item) => {
|
|
427
|
-
|
|
454
|
+
this.attributeChangedCallback(item.name, null, item.value);
|
|
428
|
-
|
|
455
|
+
});
|
|
456
|
+
}
|
|
429
457
|
}
|
|
430
|
-
}
|
|
431
458
|
|
|
432
|
-
|
|
459
|
+
static get observedAttributes() {
|
|
433
|
-
|
|
460
|
+
return keys;
|
|
434
|
-
|
|
461
|
+
}
|
|
462
|
+
},
|
|
435
463
|
};
|
|
436
464
|
if (isBrowser) {
|
|
437
465
|
if (window.customElements.get(name)) {
|
|
438
466
|
return;
|
|
467
|
+
} else {
|
|
468
|
+
window.customElements.define(name, registry[name].clazz);
|
|
439
469
|
}
|
|
440
|
-
window.customElements.define(name, registry[name]);
|
|
441
470
|
}
|
|
442
471
|
}
|
src/lit-html-server.js
CHANGED
|
@@ -1,230 +1,3 @@
|
|
|
1
|
-
/* SNOWPACK PROCESS POLYFILL (based on https://github.com/calvinmetcalf/node-process-es6) */
|
|
2
|
-
function defaultSetTimout() {
|
|
3
|
-
throw new Error('setTimeout has not been defined');
|
|
4
|
-
}
|
|
5
|
-
function defaultClearTimeout() {
|
|
6
|
-
throw new Error('clearTimeout has not been defined');
|
|
7
|
-
}
|
|
8
|
-
var cachedSetTimeout = defaultSetTimout;
|
|
9
|
-
var cachedClearTimeout = defaultClearTimeout;
|
|
10
|
-
var globalContext;
|
|
11
|
-
if (typeof window !== 'undefined') {
|
|
12
|
-
globalContext = window;
|
|
13
|
-
} else if (typeof self !== 'undefined') {
|
|
14
|
-
globalContext = self;
|
|
15
|
-
} else {
|
|
16
|
-
globalContext = {};
|
|
17
|
-
}
|
|
18
|
-
if (typeof globalContext.setTimeout === 'function') {
|
|
19
|
-
cachedSetTimeout = setTimeout;
|
|
20
|
-
}
|
|
21
|
-
if (typeof globalContext.clearTimeout === 'function') {
|
|
22
|
-
cachedClearTimeout = clearTimeout;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function runTimeout(fun) {
|
|
26
|
-
if (cachedSetTimeout === setTimeout) {
|
|
27
|
-
//normal enviroments in sane situations
|
|
28
|
-
return setTimeout(fun, 0);
|
|
29
|
-
}
|
|
30
|
-
// if setTimeout wasn't available but was latter defined
|
|
31
|
-
if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
|
|
32
|
-
cachedSetTimeout = setTimeout;
|
|
33
|
-
return setTimeout(fun, 0);
|
|
34
|
-
}
|
|
35
|
-
try {
|
|
36
|
-
// when when somebody has screwed with setTimeout but no I.E. maddness
|
|
37
|
-
return cachedSetTimeout(fun, 0);
|
|
38
|
-
} catch (e) {
|
|
39
|
-
try {
|
|
40
|
-
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
|
41
|
-
return cachedSetTimeout.call(null, fun, 0);
|
|
42
|
-
} catch (e) {
|
|
43
|
-
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
|
|
44
|
-
return cachedSetTimeout.call(this, fun, 0);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
function runClearTimeout(marker) {
|
|
51
|
-
if (cachedClearTimeout === clearTimeout) {
|
|
52
|
-
//normal enviroments in sane situations
|
|
53
|
-
return clearTimeout(marker);
|
|
54
|
-
}
|
|
55
|
-
// if clearTimeout wasn't available but was latter defined
|
|
56
|
-
if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
|
|
57
|
-
cachedClearTimeout = clearTimeout;
|
|
58
|
-
return clearTimeout(marker);
|
|
59
|
-
}
|
|
60
|
-
try {
|
|
61
|
-
// when when somebody has screwed with setTimeout but no I.E. maddness
|
|
62
|
-
return cachedClearTimeout(marker);
|
|
63
|
-
} catch (e) {
|
|
64
|
-
try {
|
|
65
|
-
// When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
|
66
|
-
return cachedClearTimeout.call(null, marker);
|
|
67
|
-
} catch (e) {
|
|
68
|
-
// same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
|
|
69
|
-
// Some versions of I.E. have different rules for clearTimeout vs setTimeout
|
|
70
|
-
return cachedClearTimeout.call(this, marker);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
var queue = [];
|
|
78
|
-
var draining = false;
|
|
79
|
-
var currentQueue;
|
|
80
|
-
var queueIndex = -1;
|
|
81
|
-
|
|
82
|
-
function cleanUpNextTick() {
|
|
83
|
-
if (!draining || !currentQueue) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
draining = false;
|
|
87
|
-
if (currentQueue.length) {
|
|
88
|
-
queue = currentQueue.concat(queue);
|
|
89
|
-
} else {
|
|
90
|
-
queueIndex = -1;
|
|
91
|
-
}
|
|
92
|
-
if (queue.length) {
|
|
93
|
-
drainQueue();
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function drainQueue() {
|
|
98
|
-
if (draining) {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
var timeout = runTimeout(cleanUpNextTick);
|
|
102
|
-
draining = true;
|
|
103
|
-
|
|
104
|
-
var len = queue.length;
|
|
105
|
-
while (len) {
|
|
106
|
-
currentQueue = queue;
|
|
107
|
-
queue = [];
|
|
108
|
-
while (++queueIndex < len) {
|
|
109
|
-
if (currentQueue) {
|
|
110
|
-
currentQueue[queueIndex].run();
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
queueIndex = -1;
|
|
114
|
-
len = queue.length;
|
|
115
|
-
}
|
|
116
|
-
currentQueue = null;
|
|
117
|
-
draining = false;
|
|
118
|
-
runClearTimeout(timeout);
|
|
119
|
-
}
|
|
120
|
-
function nextTick(fun) {
|
|
121
|
-
var args = new Array(arguments.length - 1);
|
|
122
|
-
if (arguments.length > 1) {
|
|
123
|
-
for (var i = 1; i < arguments.length; i++) {
|
|
124
|
-
args[i - 1] = arguments[i];
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
queue.push(new Item(fun, args));
|
|
128
|
-
if (queue.length === 1 && !draining) {
|
|
129
|
-
runTimeout(drainQueue);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
// v8 likes predictible objects
|
|
133
|
-
function Item(fun, array) {
|
|
134
|
-
this.fun = fun;
|
|
135
|
-
this.array = array;
|
|
136
|
-
}
|
|
137
|
-
Item.prototype.run = function () {
|
|
138
|
-
this.fun.apply(null, this.array);
|
|
139
|
-
};
|
|
140
|
-
var title = 'browser';
|
|
141
|
-
var platform = 'browser';
|
|
142
|
-
var browser = true;
|
|
143
|
-
var argv = [];
|
|
144
|
-
var version = ''; // empty string to avoid regexp issues
|
|
145
|
-
var versions = {};
|
|
146
|
-
var release = {};
|
|
147
|
-
var config = {};
|
|
148
|
-
|
|
149
|
-
function noop() { }
|
|
150
|
-
|
|
151
|
-
var on = noop;
|
|
152
|
-
var addListener = noop;
|
|
153
|
-
var once = noop;
|
|
154
|
-
var off = noop;
|
|
155
|
-
var removeListener = noop;
|
|
156
|
-
var removeAllListeners = noop;
|
|
157
|
-
var emit = noop;
|
|
158
|
-
|
|
159
|
-
function binding(name) {
|
|
160
|
-
throw new Error('process.binding is not supported');
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function cwd() { return '/' }
|
|
164
|
-
function chdir(dir) {
|
|
165
|
-
throw new Error('process.chdir is not supported');
|
|
166
|
-
} function umask() { return 0; }
|
|
167
|
-
|
|
168
|
-
// from https://github.com/kumavis/browser-process-hrtime/blob/master/index.js
|
|
169
|
-
var performance = globalContext.performance || {};
|
|
170
|
-
var performanceNow =
|
|
171
|
-
performance.now ||
|
|
172
|
-
performance.mozNow ||
|
|
173
|
-
performance.msNow ||
|
|
174
|
-
performance.oNow ||
|
|
175
|
-
performance.webkitNow ||
|
|
176
|
-
function () { return (new Date()).getTime() };
|
|
177
|
-
|
|
178
|
-
// generate timestamp or delta
|
|
179
|
-
// see http://nodejs.org/api/process.html#process_process_hrtime
|
|
180
|
-
function hrtime(previousTimestamp) {
|
|
181
|
-
var clocktime = performanceNow.call(performance) * 1e-3;
|
|
182
|
-
var seconds = Math.floor(clocktime);
|
|
183
|
-
var nanoseconds = Math.floor((clocktime % 1) * 1e9);
|
|
184
|
-
if (previousTimestamp) {
|
|
185
|
-
seconds = seconds - previousTimestamp[0];
|
|
186
|
-
nanoseconds = nanoseconds - previousTimestamp[1];
|
|
187
|
-
if (nanoseconds < 0) {
|
|
188
|
-
seconds--;
|
|
189
|
-
nanoseconds += 1e9;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
return [seconds, nanoseconds]
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
var startTime = new Date();
|
|
196
|
-
function uptime() {
|
|
197
|
-
var currentTime = new Date();
|
|
198
|
-
var dif = currentTime - startTime;
|
|
199
|
-
return dif / 1000;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
var process = {
|
|
203
|
-
nextTick: nextTick,
|
|
204
|
-
title: title,
|
|
205
|
-
browser: browser,
|
|
206
|
-
env: { "NODE_ENV": "production" },
|
|
207
|
-
argv: argv,
|
|
208
|
-
version: version,
|
|
209
|
-
versions: versions,
|
|
210
|
-
on: on,
|
|
211
|
-
addListener: addListener,
|
|
212
|
-
once: once,
|
|
213
|
-
off: off,
|
|
214
|
-
removeListener: removeListener,
|
|
215
|
-
removeAllListeners: removeAllListeners,
|
|
216
|
-
emit: emit,
|
|
217
|
-
binding: binding,
|
|
218
|
-
cwd: cwd,
|
|
219
|
-
chdir: chdir,
|
|
220
|
-
umask: umask,
|
|
221
|
-
hrtime: hrtime,
|
|
222
|
-
platform: platform,
|
|
223
|
-
release: release,
|
|
224
|
-
config: config,
|
|
225
|
-
uptime: uptime
|
|
226
|
-
};
|
|
227
|
-
|
|
228
1
|
/**
|
|
229
2
|
* Collection of shared utilities used by directives.
|
|
230
3
|
* Manually added to ensure that directives can be used by both index.js and browser.js
|
|
@@ -285,153 +58,6 @@ function isDirective(fn) {
|
|
|
285
58
|
return typeof fn === 'function' && fn.isDirective;
|
|
286
59
|
}
|
|
287
60
|
|
|
288
|
-
/**
|
|
289
|
-
* Define new directive for "fn".
|
|
290
|
-
* The passed function should be a factory function,
|
|
291
|
-
* and must return a function that will eventually be called with a Part instance
|
|
292
|
-
*
|
|
293
|
-
* @param { (...args: Array<unknown>) => (part: Part) => void } fn
|
|
294
|
-
* @returns { (...args: Array<unknown>) => (part: Part) => void }
|
|
295
|
-
*/
|
|
296
|
-
function directive(fn) {
|
|
297
|
-
return function directive(...args) {
|
|
298
|
-
const result = fn(...args);
|
|
299
|
-
|
|
300
|
-
if (typeof result !== 'function') {
|
|
301
|
-
throw Error('directives are factory functions and must return a function when called');
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// @ts-ignore
|
|
305
|
-
result.isDirective = true;
|
|
306
|
-
return result;
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
/* global ReadableStream */
|
|
311
|
-
/**
|
|
312
|
-
* A custom Readable stream factory for rendering a template result to a stream
|
|
313
|
-
*
|
|
314
|
-
* @param { TemplateResult } result - a template result returned from call to "html`...`"
|
|
315
|
-
* @param { TemplateResultProcessor } processor
|
|
316
|
-
* @param { RenderOptions } [options]
|
|
317
|
-
* @returns { ReadableStream }
|
|
318
|
-
*/
|
|
319
|
-
function browserStreamTemplateRenderer(result, processor, options) {
|
|
320
|
-
if (typeof ReadableStream === 'undefined') {
|
|
321
|
-
throw Error('ReadableStream not supported on this platform');
|
|
322
|
-
}
|
|
323
|
-
if (typeof TextEncoder === 'undefined') {
|
|
324
|
-
throw Error('TextEncoder not supported on this platform');
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return new ReadableStream({
|
|
328
|
-
// @ts-ignore
|
|
329
|
-
process: null,
|
|
330
|
-
start(controller) {
|
|
331
|
-
const encoder = new TextEncoder();
|
|
332
|
-
const underlyingSource = this;
|
|
333
|
-
let stack = [result];
|
|
334
|
-
|
|
335
|
-
this.process = processor.getProcessor(
|
|
336
|
-
{
|
|
337
|
-
push(chunk) {
|
|
338
|
-
if (chunk === null) {
|
|
339
|
-
controller.close();
|
|
340
|
-
return false;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
controller.enqueue(encoder.encode(chunk.toString()));
|
|
344
|
-
// Pause processing (return "false") if stream is full
|
|
345
|
-
return controller.desiredSize != null ? controller.desiredSize > 0 : true;
|
|
346
|
-
},
|
|
347
|
-
destroy(err) {
|
|
348
|
-
controller.error(err);
|
|
349
|
-
underlyingSource.process = undefined;
|
|
350
|
-
// @ts-ignore
|
|
351
|
-
stack = undefined;
|
|
352
|
-
},
|
|
353
|
-
},
|
|
354
|
-
stack,
|
|
355
|
-
16384,
|
|
356
|
-
options
|
|
357
|
-
);
|
|
358
|
-
},
|
|
359
|
-
pull() {
|
|
360
|
-
this.process();
|
|
361
|
-
},
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* A minimal Buffer class that implements a partial brower Buffer API around strings.
|
|
367
|
-
* This module should be provided as an alias for Node's built-in 'buffer' module
|
|
368
|
-
* when run in the browser.
|
|
369
|
-
* @see package.json#browser
|
|
370
|
-
*/
|
|
371
|
-
class Buffer {
|
|
372
|
-
/**
|
|
373
|
-
* Determine if 'buffer' is a buffer
|
|
374
|
-
*
|
|
375
|
-
* @param { any } buffer
|
|
376
|
-
* @returns { boolean }
|
|
377
|
-
*/
|
|
378
|
-
static isBuffer(buffer) {
|
|
379
|
-
return buffer != null && typeof buffer === 'object' && buffer.string !== undefined;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Create buffer from 'string'
|
|
384
|
-
*
|
|
385
|
-
* @param { string } string
|
|
386
|
-
* @returns { Buffer }
|
|
387
|
-
*/
|
|
388
|
-
static from(string) {
|
|
389
|
-
string = typeof string === 'string' ? string : String(string);
|
|
390
|
-
return new Buffer(string);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Join 'buffers' into a single string
|
|
395
|
-
*
|
|
396
|
-
* @param { Array<any> } buffers
|
|
397
|
-
* @param { number } [length]
|
|
398
|
-
* @returns { Buffer }
|
|
399
|
-
*/
|
|
400
|
-
static concat(buffers, length) {
|
|
401
|
-
let string = '';
|
|
402
|
-
|
|
403
|
-
for (let i = 0, n = buffers.length; i < n; i++) {
|
|
404
|
-
const buffer = buffers[i];
|
|
405
|
-
|
|
406
|
-
string += typeof buffer === 'string' ? buffer : String(buffer);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
if (length !== undefined && string.length > length) {
|
|
410
|
-
string = string.slice(0, length);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
return new Buffer(string);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* Construct Buffer instance
|
|
418
|
-
*
|
|
419
|
-
* @param { string } string
|
|
420
|
-
*/
|
|
421
|
-
constructor(string) {
|
|
422
|
-
this.string = string;
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* Stringify
|
|
427
|
-
*
|
|
428
|
-
* @returns { string }
|
|
429
|
-
*/
|
|
430
|
-
toString() {
|
|
431
|
-
return this.string;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
61
|
/**
|
|
436
62
|
* Determine whether "result" is a TemplateResult
|
|
437
63
|
*
|
|
@@ -443,17 +69,6 @@ function isTemplateResult(result) {
|
|
|
443
69
|
return result && typeof result.template !== 'undefined' && typeof result.values !== 'undefined';
|
|
444
70
|
}
|
|
445
71
|
|
|
446
|
-
/**
|
|
447
|
-
* Determine if "promise" is a Promise instance
|
|
448
|
-
*
|
|
449
|
-
* @param { unknown } promise
|
|
450
|
-
* @returns { promise is Promise<unknown> }
|
|
451
|
-
*/
|
|
452
|
-
function isPromise(promise) {
|
|
453
|
-
// @ts-ignore
|
|
454
|
-
return promise != null && promise.then != null;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
72
|
/**
|
|
458
73
|
* Determine if "iterator" is an synchronous iterator
|
|
459
74
|
*
|
|
@@ -470,28 +85,6 @@ function isSyncIterator(iterator) {
|
|
|
470
85
|
);
|
|
471
86
|
}
|
|
472
87
|
|
|
473
|
-
/**
|
|
474
|
-
* Determine if "iterator" is an asynchronous iterator
|
|
475
|
-
*
|
|
476
|
-
* @param { unknown } iterator
|
|
477
|
-
* @returns { iterator is AsyncIterable<unknown> }
|
|
478
|
-
*/
|
|
479
|
-
function isAsyncIterator(iterator) {
|
|
480
|
-
// @ts-ignore
|
|
481
|
-
return iterator != null && typeof iterator[Symbol.asyncIterator] === 'function';
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
/**
|
|
485
|
-
* Determine if "result" is an iterator result object
|
|
486
|
-
*
|
|
487
|
-
* @param { unknown } result
|
|
488
|
-
* @returns { result is IteratorResult<unknown, unknown> }
|
|
489
|
-
*/
|
|
490
|
-
function isIteratorResult(result) {
|
|
491
|
-
// @ts-ignore
|
|
492
|
-
return result != null && typeof result === 'object' && 'value' in result && 'done' in result;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
88
|
/**
|
|
496
89
|
* Determine if "value" is an object
|
|
497
90
|
*
|
|
@@ -596,7 +189,7 @@ class Part {
|
|
|
596
189
|
/**
|
|
597
190
|
* No-op
|
|
598
191
|
*/
|
|
599
|
-
commit() {
|
|
192
|
+
commit() {}
|
|
600
193
|
}
|
|
601
194
|
|
|
602
195
|
/**
|
|
@@ -652,11 +245,7 @@ class AttributePart extends Part {
|
|
|
652
245
|
|
|
653
246
|
for (let i = 0; i < this.length; i++) {
|
|
654
247
|
const string = this.strings[i];
|
|
655
|
-
let value = resolveAttributeValue(
|
|
656
|
-
values[i],
|
|
657
|
-
this,
|
|
658
|
-
|
|
248
|
+
let value = resolveAttributeValue(values[i], this, options !== undefined ? options.serializePropertyAttributes : false);
|
|
659
|
-
);
|
|
660
249
|
|
|
661
250
|
// Bail if 'nothing'
|
|
662
251
|
if (value === nothing) {
|
|
@@ -669,23 +258,6 @@ class AttributePart extends Part {
|
|
|
669
258
|
if (isBuffer(value)) {
|
|
670
259
|
chunks.push(value);
|
|
671
260
|
chunkLength += value.length;
|
|
672
|
-
} else if (isPromise(value)) {
|
|
673
|
-
// Lazy init for uncommon scenario
|
|
674
|
-
if (pendingChunks === undefined) {
|
|
675
|
-
pendingChunks = [];
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
// @ts-ignore
|
|
679
|
-
const index = chunks.push(value) - 1;
|
|
680
|
-
|
|
681
|
-
pendingChunks.push(
|
|
682
|
-
value.then((value) => {
|
|
683
|
-
// @ts-ignore
|
|
684
|
-
chunks[index] = value;
|
|
685
|
-
// @ts-ignore
|
|
686
|
-
chunkLength += value.length;
|
|
687
|
-
})
|
|
688
|
-
);
|
|
689
261
|
} else if (isArray(value)) {
|
|
690
262
|
for (const chunk of value) {
|
|
691
263
|
chunks.push(chunk);
|
|
@@ -721,11 +293,7 @@ class BooleanAttributePart extends AttributePart {
|
|
|
721
293
|
|
|
722
294
|
this.nameAsBuffer = Buffer.from(this.name);
|
|
723
295
|
|
|
724
|
-
if (
|
|
725
|
-
strings.length !== 2 ||
|
|
726
|
-
|
|
296
|
+
if (strings.length !== 2 || strings[0] === EMPTY_STRING_BUFFER || strings[1] === EMPTY_STRING_BUFFER) {
|
|
727
|
-
strings[1] === EMPTY_STRING_BUFFER
|
|
728
|
-
) {
|
|
729
297
|
throw Error('Boolean attributes can only contain a single expression');
|
|
730
298
|
}
|
|
731
299
|
}
|
|
@@ -744,10 +312,6 @@ class BooleanAttributePart extends AttributePart {
|
|
|
744
312
|
value = resolveDirectiveValue(value, this);
|
|
745
313
|
}
|
|
746
314
|
|
|
747
|
-
if (isPromise(value)) {
|
|
748
|
-
return value.then((value) => (value ? this.nameAsBuffer : EMPTY_STRING_BUFFER));
|
|
749
|
-
}
|
|
750
|
-
|
|
751
315
|
return value ? this.nameAsBuffer : EMPTY_STRING_BUFFER;
|
|
752
316
|
}
|
|
753
317
|
}
|
|
@@ -770,9 +334,7 @@ class PropertyAttributePart extends AttributePart {
|
|
|
770
334
|
const value = super.getValue(values, options);
|
|
771
335
|
const prefix = Buffer.from('.');
|
|
772
336
|
|
|
773
|
-
return value instanceof Promise
|
|
774
|
-
? value.then((value) => Buffer.concat([prefix, value]))
|
|
775
|
-
|
|
337
|
+
return Buffer.concat([prefix, value]);
|
|
776
338
|
}
|
|
777
339
|
|
|
778
340
|
return EMPTY_STRING_BUFFER;
|
|
@@ -818,17 +380,13 @@ function resolveAttributeValue(value, part, serialiseObjectsAndArrays = false) {
|
|
|
818
380
|
if (isPrimitive(value)) {
|
|
819
381
|
const string = typeof value !== 'string' ? String(value) : value;
|
|
820
382
|
// Escape if not prefixed with unsafePrefixString, otherwise strip prefix
|
|
821
|
-
return Buffer.from(
|
|
822
|
-
|
|
383
|
+
return Buffer.from(string.indexOf(unsafePrefixString) === 0 ? string.slice(33) : escape(string, 'attribute'));
|
|
823
|
-
);
|
|
824
384
|
} else if (typeof value === 'object') {
|
|
825
385
|
return Buffer.from(JSON.stringify(value).replace(/"/g, `'`));
|
|
826
386
|
} else if (isBuffer(value)) {
|
|
827
387
|
return value;
|
|
828
388
|
} else if (serialiseObjectsAndArrays && (isObject(value) || isArray(value))) {
|
|
829
389
|
return Buffer.from(escape(JSON.stringify(value), 'attribute'));
|
|
830
|
-
} else if (isPromise(value)) {
|
|
831
|
-
return value.then((value) => resolveAttributeValue(value, part, serialiseObjectsAndArrays));
|
|
832
390
|
} else if (isSyncIterator(value)) {
|
|
833
391
|
if (!isArray(value)) {
|
|
834
392
|
value = Array.from(value);
|
|
@@ -843,7 +401,7 @@ function resolveAttributeValue(value, part, serialiseObjectsAndArrays = false) {
|
|
|
843
401
|
}
|
|
844
402
|
values.push(value);
|
|
845
403
|
return values;
|
|
846
|
-
}, [])
|
|
404
|
+
}, []),
|
|
847
405
|
);
|
|
848
406
|
} else {
|
|
849
407
|
return Buffer.from(String(value));
|
|
@@ -872,15 +430,10 @@ function resolveNodeValue(value, part) {
|
|
|
872
430
|
return Buffer.from(
|
|
873
431
|
string.indexOf(unsafePrefixString) === 0
|
|
874
432
|
? string.slice(33)
|
|
875
|
-
: escape(
|
|
876
|
-
string,
|
|
877
|
-
|
|
433
|
+
: escape(string, part.tagName === 'script' || part.tagName === 'style' ? part.tagName : 'text'),
|
|
878
|
-
)
|
|
879
434
|
);
|
|
880
435
|
} else if (isTemplateResult(value) || isBuffer(value)) {
|
|
881
436
|
return value;
|
|
882
|
-
} else if (isPromise(value)) {
|
|
883
|
-
return value.then((value) => resolveNodeValue(value, part));
|
|
884
437
|
} else if (isSyncIterator(value)) {
|
|
885
438
|
if (!isArray(value)) {
|
|
886
439
|
value = Array.from(value);
|
|
@@ -895,8 +448,6 @@ function resolveNodeValue(value, part) {
|
|
|
895
448
|
values.push(value);
|
|
896
449
|
return values;
|
|
897
450
|
}, []);
|
|
898
|
-
} else if (isAsyncIterator(value)) {
|
|
899
|
-
return resolveAsyncIteratorValue(value, part);
|
|
900
451
|
} else {
|
|
901
452
|
throw Error(`unknown NodePart value: ${value}`);
|
|
902
453
|
}
|
|
@@ -1035,37 +586,6 @@ class DefaultTemplateResultProcessor {
|
|
|
1035
586
|
breakLoop = !flushBuffer();
|
|
1036
587
|
processing = !breakLoop;
|
|
1037
588
|
}
|
|
1038
|
-
} else if (isPromise(chunk)) {
|
|
1039
|
-
// Flush buffered data before waiting for Promise
|
|
1040
|
-
flushBuffer();
|
|
1041
|
-
// "processing" is still true, so prevented from restarting until Promise resolved
|
|
1042
|
-
breakLoop = true;
|
|
1043
|
-
// Add pending Promise for value to stack
|
|
1044
|
-
stack.unshift(chunk);
|
|
1045
|
-
chunk
|
|
1046
|
-
.then((chunk) => {
|
|
1047
|
-
// Handle IteratorResults from AsyncIterator
|
|
1048
|
-
if (isIteratorResult(chunk)) {
|
|
1049
|
-
if (chunk.done) {
|
|
1050
|
-
// Clear resolved Promise
|
|
1051
|
-
stack.shift();
|
|
1052
|
-
// Clear AsyncIterator
|
|
1053
|
-
stack.shift();
|
|
1054
|
-
} else {
|
|
1055
|
-
// Replace resolved Promise with IteratorResult value
|
|
1056
|
-
stack[0] = chunk.value;
|
|
1057
|
-
}
|
|
1058
|
-
} else {
|
|
1059
|
-
// Replace resolved Promise with value
|
|
1060
|
-
stack[0] = chunk;
|
|
1061
|
-
}
|
|
1062
|
-
processing = false;
|
|
1063
|
-
process();
|
|
1064
|
-
})
|
|
1065
|
-
.catch((err) => {
|
|
1066
|
-
stack.length = 0;
|
|
1067
|
-
renderer.destroy(err);
|
|
1068
|
-
});
|
|
1069
589
|
} else if (isArray(chunk)) {
|
|
1070
590
|
// First remove existing Array if at top of stack (not added by pending TemplateResult)
|
|
1071
591
|
if (stack[0] === chunk) {
|
|
@@ -1073,14 +593,6 @@ class DefaultTemplateResultProcessor {
|
|
|
1073
593
|
stack.shift();
|
|
1074
594
|
}
|
|
1075
595
|
stack.unshift(...chunk);
|
|
1076
|
-
} else if (isAsyncIterator(chunk)) {
|
|
1077
|
-
popStack = false;
|
|
1078
|
-
// Add AsyncIterator back to stack (will be cleared when done iterating)
|
|
1079
|
-
if (stack[0] !== chunk) {
|
|
1080
|
-
stack.unshift(chunk);
|
|
1081
|
-
}
|
|
1082
|
-
// Add pending Promise for IteratorResult to stack
|
|
1083
|
-
stack.unshift(chunk[Symbol.asyncIterator]().next());
|
|
1084
596
|
} else {
|
|
1085
597
|
stack.length = 0;
|
|
1086
598
|
return renderer.destroy(Error(`unknown chunk type: ${chunk}`));
|
|
@@ -1165,96 +677,11 @@ function promiseTemplateRenderer(result, processor, asBuffer = false, options) {
|
|
|
1165
677
|
},
|
|
1166
678
|
stack,
|
|
1167
679
|
0,
|
|
1168
|
-
options
|
|
680
|
+
options,
|
|
1169
681
|
)();
|
|
1170
682
|
});
|
|
1171
683
|
}
|
|
1172
684
|
|
|
1173
|
-
/**
|
|
1174
|
-
* A minimal Readable stream class to prevent browser parse errors resulting from
|
|
1175
|
-
* the static importing of Node-only code.
|
|
1176
|
-
* This module should be provided as an alias for Node's built-in 'stream' module
|
|
1177
|
-
* when run in the browser.
|
|
1178
|
-
* @see package.json#browser
|
|
1179
|
-
*/
|
|
1180
|
-
class Readable { }
|
|
1181
|
-
|
|
1182
|
-
/**
|
|
1183
|
-
* Factory for StreamTemplateRenderer instances
|
|
1184
|
-
*
|
|
1185
|
-
* @param { TemplateResult } result - a template result returned from call to "html`...`"
|
|
1186
|
-
* @param { TemplateResultProcessor } processor
|
|
1187
|
-
* @param { RenderOptions } [options]
|
|
1188
|
-
* @returns { Readable }
|
|
1189
|
-
*/
|
|
1190
|
-
function streamTemplateRenderer(result, processor, options) {
|
|
1191
|
-
return new StreamTemplateRenderer(result, processor, options);
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
/**
|
|
1195
|
-
* A custom Readable stream class for rendering a template result to a stream
|
|
1196
|
-
*/
|
|
1197
|
-
class StreamTemplateRenderer extends Readable {
|
|
1198
|
-
/**
|
|
1199
|
-
* Constructor
|
|
1200
|
-
*
|
|
1201
|
-
* @param { TemplateResult } result - a template result returned from call to "html`...`"
|
|
1202
|
-
* @param { TemplateResultProcessor } processor
|
|
1203
|
-
* @param { RenderOptions } [options]
|
|
1204
|
-
*/
|
|
1205
|
-
constructor(result, processor, options) {
|
|
1206
|
-
super({ autoDestroy: true });
|
|
1207
|
-
|
|
1208
|
-
this.stack = [result];
|
|
1209
|
-
this.process = processor.getProcessor(this, this.stack, 16384, options);
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
/**
|
|
1213
|
-
* Extend Readable.read()
|
|
1214
|
-
*/
|
|
1215
|
-
_read() {
|
|
1216
|
-
if (this.process !== undefined) {
|
|
1217
|
-
this.process();
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
/**
|
|
1222
|
-
* Extend Readalbe.destroy()
|
|
1223
|
-
*
|
|
1224
|
-
* @param { Error | null } [err]
|
|
1225
|
-
*/
|
|
1226
|
-
_destroy(err) {
|
|
1227
|
-
if (err) {
|
|
1228
|
-
this.emit('error', err);
|
|
1229
|
-
}
|
|
1230
|
-
this.emit('close');
|
|
1231
|
-
|
|
1232
|
-
// @ts-ignore
|
|
1233
|
-
this.process = undefined;
|
|
1234
|
-
// @ts-ignore
|
|
1235
|
-
this.stack = undefined;
|
|
1236
|
-
this.removeAllListeners();
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
/**
|
|
1241
|
-
* @license
|
|
1242
|
-
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
|
1243
|
-
* This code may only be used under the BSD style license found at
|
|
1244
|
-
* http://polymer.github.io/LICENSE.txt
|
|
1245
|
-
* The complete set of authors may be found at
|
|
1246
|
-
* http://polymer.github.io/AUTHORS.txt
|
|
1247
|
-
* The complete set of contributors may be found at
|
|
1248
|
-
* http://polymer.github.io/CONTRIBUTORS.txt
|
|
1249
|
-
* Code distributed by Google as part of the polymer project is also
|
|
1250
|
-
* subject to an additional IP rights grant found at
|
|
1251
|
-
* http://polymer.github.io/PATENTS.txt
|
|
1252
|
-
*/
|
|
1253
|
-
/**
|
|
1254
|
-
* An expression marker with embedded unique key to avoid collision with
|
|
1255
|
-
* possible text in templates.
|
|
1256
|
-
*/
|
|
1257
|
-
const marker = `{{lit-${String(Math.random()).slice(2)}}}`;
|
|
1258
685
|
/**
|
|
1259
686
|
* This regex extracts the attribute name preceding an attribute-position
|
|
1260
687
|
* expression. It does this by matching the syntax allowed for attributes
|
|
@@ -1305,11 +732,8 @@ class Template {
|
|
|
1305
732
|
* @param { TemplateProcessor } processor
|
|
1306
733
|
*/
|
|
1307
734
|
constructor(strings, processor) {
|
|
1308
|
-
/** @type { Array<Buffer | null> } */
|
|
1309
735
|
this.strings = [];
|
|
1310
|
-
/** @type { Array<Part | null> } */
|
|
1311
736
|
this.parts = [];
|
|
1312
|
-
|
|
1313
737
|
this._prepare(strings, processor);
|
|
1314
738
|
}
|
|
1315
739
|
|
|
@@ -1381,11 +805,7 @@ class Template {
|
|
|
1381
805
|
|
|
1382
806
|
part = processor.handleAttributeExpressions(name, attributeStrings, tagName);
|
|
1383
807
|
} else {
|
|
1384
|
-
part = processor.handleAttributeExpressions(
|
|
1385
|
-
name,
|
|
1386
|
-
|
|
808
|
+
part = processor.handleAttributeExpressions(name, [EMPTY_STRING_BUFFER$1, EMPTY_STRING_BUFFER$1], tagName);
|
|
1387
|
-
tagName
|
|
1388
|
-
);
|
|
1389
809
|
}
|
|
1390
810
|
}
|
|
1391
811
|
} else {
|
|
@@ -1578,9 +998,6 @@ function reduce(buffer, chunks, chunk) {
|
|
|
1578
998
|
return EMPTY_STRING_BUFFER$2;
|
|
1579
999
|
} else if (isArray(chunk)) {
|
|
1580
1000
|
return chunk.reduce((buffer, chunk) => reduce(buffer, chunks, chunk), buffer);
|
|
1581
|
-
} else if (isPromise(chunk) || isAsyncIterator(chunk)) {
|
|
1582
|
-
chunks.push(buffer, chunk);
|
|
1583
|
-
return EMPTY_STRING_BUFFER$2;
|
|
1584
1001
|
}
|
|
1585
1002
|
}
|
|
1586
1003
|
|
|
@@ -1596,12 +1013,6 @@ const DEFAULT_TEMPLATE_FN = (value) => html`${value}`;
|
|
|
1596
1013
|
const defaultTemplateProcessor = new DefaultTemplateProcessor();
|
|
1597
1014
|
const defaultTemplateResultProcessor = new DefaultTemplateResultProcessor();
|
|
1598
1015
|
const templateCache = new Map();
|
|
1599
|
-
const streamRenderer =
|
|
1600
|
-
typeof process !== 'undefined' &&
|
|
1601
|
-
typeof process.versions !== 'undefined' &&
|
|
1602
|
-
typeof process.versions.node !== 'undefined'
|
|
1603
|
-
? streamTemplateRenderer
|
|
1604
|
-
: browserStreamTemplateRenderer;
|
|
1605
1016
|
|
|
1606
1017
|
/**
|
|
1607
1018
|
* Interprets a template literal as an HTML template that can be
|
|
@@ -1622,17 +1033,6 @@ function html(strings, ...values) {
|
|
|
1622
1033
|
return new TemplateResult(template, values);
|
|
1623
1034
|
}
|
|
1624
1035
|
|
|
1625
|
-
/**
|
|
1626
|
-
* Render a template result to a Readable stream
|
|
1627
|
-
*
|
|
1628
|
-
* @param { unknown } result - a template result returned from call to "html`...`"
|
|
1629
|
-
* @param { RenderOptions } [options]
|
|
1630
|
-
* @returns { import('stream').Readable | ReadableStream }
|
|
1631
|
-
*/
|
|
1632
|
-
function renderToStream(result, options) {
|
|
1633
|
-
return streamRenderer(getRenderResult(result), defaultTemplateResultProcessor, options);
|
|
1634
|
-
}
|
|
1635
|
-
|
|
1636
1036
|
/**
|
|
1637
1037
|
* Render a template result to a string resolving Promise.
|
|
1638
1038
|
*
|
|
@@ -1641,28 +1041,7 @@ function renderToStream(result, options) {
|
|
|
1641
1041
|
* @returns { Promise<string> }
|
|
1642
1042
|
*/
|
|
1643
1043
|
function renderToString(result, options) {
|
|
1644
|
-
return promiseTemplateRenderer(
|
|
1645
|
-
getRenderResult(result),
|
|
1646
|
-
defaultTemplateResultProcessor,
|
|
1647
|
-
false,
|
|
1648
|
-
options
|
|
1649
|
-
);
|
|
1650
|
-
}
|
|
1651
|
-
|
|
1652
|
-
/**
|
|
1653
|
-
* Render a template result to a Buffer resolving Promise.
|
|
1654
|
-
*
|
|
1655
|
-
|
|
1044
|
+
return promiseTemplateRenderer(getRenderResult(result), defaultTemplateResultProcessor, false, options);
|
|
1656
|
-
* @param { RenderOptions } [options]
|
|
1657
|
-
* @returns { Promise<Buffer> }
|
|
1658
|
-
*/
|
|
1659
|
-
function renderToBuffer(result, options) {
|
|
1660
|
-
return promiseTemplateRenderer(
|
|
1661
|
-
getRenderResult(result),
|
|
1662
|
-
defaultTemplateResultProcessor,
|
|
1663
|
-
true,
|
|
1664
|
-
options
|
|
1665
|
-
);
|
|
1666
1045
|
}
|
|
1667
1046
|
|
|
1668
1047
|
/**
|
|
@@ -1676,4 +1055,30 @@ function getRenderResult(result) {
|
|
|
1676
1055
|
return !isTemplateResult(result) ? DEFAULT_TEMPLATE_FN(result) : result;
|
|
1677
1056
|
}
|
|
1678
1057
|
|
|
1679
|
-
export {
|
|
1058
|
+
export {
|
|
1059
|
+
AttributePart,
|
|
1060
|
+
BooleanAttributePart,
|
|
1061
|
+
DefaultTemplateProcessor,
|
|
1062
|
+
DefaultTemplateResultProcessor,
|
|
1063
|
+
EventAttributePart,
|
|
1064
|
+
NodePart,
|
|
1065
|
+
Part,
|
|
1066
|
+
PropertyAttributePart,
|
|
1067
|
+
Template,
|
|
1068
|
+
TemplateResult,
|
|
1069
|
+
defaultTemplateProcessor,
|
|
1070
|
+
defaultTemplateResultProcessor,
|
|
1071
|
+
directive,
|
|
1072
|
+
html,
|
|
1073
|
+
isAttributePart,
|
|
1074
|
+
isDirective,
|
|
1075
|
+
isNodePart,
|
|
1076
|
+
isTemplateResult,
|
|
1077
|
+
nothing,
|
|
1078
|
+
renderToBuffer,
|
|
1079
|
+
renderToStream,
|
|
1080
|
+
renderToString,
|
|
1081
|
+
html as svg,
|
|
1082
|
+
templateCache,
|
|
1083
|
+
unsafePrefixString,
|
|
1084
|
+
};
|
src/lit-html.js
CHANGED
|
@@ -53,13 +53,13 @@ const directives = new WeakMap();
|
|
|
53
53
|
* });
|
|
54
54
|
*/
|
|
55
55
|
const directive =
|
|
56
|
-
f =>
|
|
56
|
+
(f) =>
|
|
57
57
|
(...args) => {
|
|
58
58
|
const d = f(...args);
|
|
59
59
|
directives.set(d, true);
|
|
60
60
|
return d;
|
|
61
61
|
};
|
|
62
|
-
const isDirective = o => {
|
|
62
|
+
const isDirective = (o) => {
|
|
63
63
|
return typeof o === 'function' && directives.has(o);
|
|
64
64
|
};
|
|
65
65
|
|
|
@@ -304,7 +304,7 @@ const endsWith = (str, suffix) => {
|
|
|
304
304
|
const index = str.length - suffix.length;
|
|
305
305
|
return index >= 0 && str.slice(index) === suffix;
|
|
306
306
|
};
|
|
307
|
-
const isTemplatePartActive = part => part.index !== -1;
|
|
307
|
+
const isTemplatePartActive = (part) => part.index !== -1;
|
|
308
308
|
// Allows `document.createComment('')` to be renamed for a
|
|
309
309
|
// small manual size-savings.
|
|
310
310
|
const createMarker = () => document.createComment('');
|
|
@@ -488,7 +488,7 @@ class TemplateInstance {
|
|
|
488
488
|
* before any untrusted expressions have been mixed in. Therefor it is
|
|
489
489
|
* considered safe by construction.
|
|
490
490
|
*/
|
|
491
|
-
const policy = typeof window !== 'undefined' && window.trustedTypes && trustedTypes.createPolicy('lit-html', { createHTML: s => s });
|
|
491
|
+
const policy = typeof window !== 'undefined' && window.trustedTypes && trustedTypes.createPolicy('lit-html', { createHTML: (s) => s });
|
|
492
492
|
const commentMarker = ` ${marker} `;
|
|
493
493
|
/**
|
|
494
494
|
* The return type of `html`, which holds a Template and the values from
|
|
@@ -601,10 +601,10 @@ class SVGTemplateResult extends TemplateResult {
|
|
|
601
601
|
* subject to an additional IP rights grant found at
|
|
602
602
|
* http://polymer.github.io/PATENTS.txt
|
|
603
603
|
*/
|
|
604
|
-
const isPrimitive = value => {
|
|
604
|
+
const isPrimitive = (value) => {
|
|
605
605
|
return value === null || !(typeof value === 'object' || typeof value === 'function');
|
|
606
606
|
};
|
|
607
|
-
const isIterable = value => {
|
|
607
|
+
const isIterable = (value) => {
|
|
608
608
|
return (
|
|
609
609
|
Array.isArray(value) ||
|
|
610
610
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -999,7 +999,7 @@ class EventPart {
|
|
|
999
999
|
this.element = element;
|
|
1000
1000
|
this.eventName = eventName;
|
|
1001
1001
|
this.eventContext = eventContext;
|
|
1002
|
-
this.__boundHandleEvent = e => this.handleEvent(e);
|
|
1002
|
+
this.__boundHandleEvent = (e) => this.handleEvent(e);
|
|
1003
1003
|
}
|
|
1004
1004
|
setValue(value) {
|
|
1005
1005
|
this.__pendingValue = value;
|
|
@@ -1041,7 +1041,7 @@ class EventPart {
|
|
|
1041
1041
|
// We copy options because of the inconsistent behavior of browsers when reading
|
|
1042
1042
|
// the third argument of add/removeEventListener. IE11 doesn't support options
|
|
1043
1043
|
// at all. Chrome 41 only reads `capture` if the argument is an object.
|
|
1044
|
-
const getOptions = o => o && (eventOptionsSupported ? { capture: o.capture, passive: o.passive, once: o.once } : o.capture);
|
|
1044
|
+
const getOptions = (o) => o && (eventOptionsSupported ? { capture: o.capture, passive: o.passive, once: o.once } : o.capture);
|
|
1045
1045
|
|
|
1046
1046
|
/**
|
|
1047
1047
|
* @license
|
test/index.test.js
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
const logMock = jest.fn();
|
|
19
19
|
setLogError(logMock);
|
|
20
20
|
|
|
21
|
-
const expectError = msg => expect(logMock).toHaveBeenCalledWith(msg);
|
|
21
|
+
const expectError = (msg) => expect(logMock).toHaveBeenCalledWith(msg);
|
|
22
22
|
|
|
23
23
|
const primitives = [
|
|
24
24
|
{
|
|
@@ -41,7 +41,7 @@ const primitives = [
|
|
|
41
41
|
},
|
|
42
42
|
];
|
|
43
43
|
|
|
44
|
-
primitives.forEach(value =>
|
|
44
|
+
primitives.forEach((value) =>
|
|
45
45
|
it(`${value.type}`, () => {
|
|
46
46
|
const context = 'key';
|
|
47
47
|
expect(value.validator.type).toEqual(value.type);
|
|
@@ -57,7 +57,7 @@ primitives.forEach(value =>
|
|
|
57
57
|
value.validator.validate(context, v);
|
|
58
58
|
expectError(`'key' Expected type '${value.type}' got type '${typeof v}'`);
|
|
59
59
|
}
|
|
60
|
-
})
|
|
60
|
+
}),
|
|
61
61
|
);
|
|
62
62
|
|
|
63
63
|
test('object', () => {
|
|
@@ -136,11 +136,23 @@ test('array', () => {
|
|
|
136
136
|
expectError(`'items[1].street' Expected type 'string' got type 'boolean'`);
|
|
137
137
|
});
|
|
138
138
|
|
|
139
|
+
test('useConfig', async () => {
|
|
140
|
+
expect(useConfig()).toBe(undefined);
|
|
141
|
+
global.config = {};
|
|
142
|
+
expect(useConfig()).toMatchObject({});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('useLocation', async () => {
|
|
146
|
+
expect(useLocation()).toBe(undefined);
|
|
147
|
+
global.location = {};
|
|
148
|
+
expect(useLocation()).toMatchObject({});
|
|
149
|
+
});
|
|
150
|
+
|
|
139
151
|
test('render', async () => {
|
|
140
152
|
const data = { name: '123', address: { street: '1' } };
|
|
141
153
|
const template = html`
|
|
142
154
|
<div>
|
|
143
|
-
<app-counter name="123" details=${data}></app-counter>
|
|
155
|
+
<app-counter name="123" details="${data}"></app-counter>
|
|
144
156
|
</div>
|
|
145
157
|
`;
|
|
146
158
|
const res = await render(template);
|
|
@@ -151,6 +163,20 @@ test('render', async () => {
|
|
|
151
163
|
`);
|
|
152
164
|
});
|
|
153
165
|
|
|
166
|
+
test('render single template', async () => {
|
|
167
|
+
const template = html` <div>${html`NoCountry ${false}`}</div> `;
|
|
168
|
+
const res = await render(template);
|
|
169
|
+
expect(res).toEqual(` <div>NoCountry false</div> `);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('render multu template', async () => {
|
|
173
|
+
const template = html` <div>${[1, 2].map((v) => html` <app-item meta="${{ index: v }}" @click=${() => {}} .handleClick=${() => {}}></app-item>`)}</div> `;
|
|
174
|
+
const res = await render(template);
|
|
175
|
+
expect(res).toEqual(
|
|
176
|
+
` <div> <app-item meta="{'index':1}" @click=undefined .handleClick=undefined></app-item> <app-item meta="{'index':2}" @click=undefined .handleClick=undefined></app-item></div> `,
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
|
|
154
180
|
test('defineElement', async () => {
|
|
155
181
|
const attrTypes = {
|
|
156
182
|
address: object({
|
|
@@ -167,8 +193,8 @@ test('defineElement', async () => {
|
|
|
167
193
|
`;
|
|
168
194
|
};
|
|
169
195
|
defineElement('app-item', AppItem, attrTypes);
|
|
170
|
-
const
|
|
196
|
+
const { clazz } = getElement('app-item');
|
|
171
|
-
const instance = new
|
|
197
|
+
const instance = new clazz([{ name: 'address', value: JSON.stringify({ street: '123' }).replace(/"/g, `'`) }]);
|
|
172
198
|
const res = await instance.render();
|
|
173
199
|
expect(res).toEqual(`
|
|
174
200
|
<div>
|
|
@@ -177,15 +203,3 @@ test('defineElement', async () => {
|
|
|
177
203
|
</div>
|
|
178
204
|
`);
|
|
179
205
|
});
|
|
180
|
-
|
|
181
|
-
test('useConfig', async () => {
|
|
182
|
-
expect(useConfig()).toBe(undefined);
|
|
183
|
-
global.config = {};
|
|
184
|
-
expect(useConfig()).toMatchObject({});
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
test('useLocation', async () => {
|
|
188
|
-
expect(useLocation()).toBe(undefined);
|
|
189
|
-
global.location = {};
|
|
190
|
-
expect(useLocation()).toMatchObject({});
|
|
191
|
-
});
|