~repos /atoms-element

#js

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
Files changed (6) hide show
  1. package.json +6 -0
  2. readme.md +4 -4
  3. src/index.js +152 -123
  4. src/lit-html-server.js +37 -632
  5. src/lit-html.js +8 -8
  6. 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
  ![Test](https://github.com/pyros2097/atoms-element/actions/workflows/main.yml/badge.svg)
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. Props are attributes on the custom element by default so its easier to debug.
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) on the client
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
- 3. [fuco](https://github.com/wtnbass/fuco)
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 as litHtml, render as litRender, directive as litDirective, NodePart, AttributePart, PropertyPart, isPrimitive } from './lit-html.js';
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 const html = isBrowser ? litHtml : litServerHtml;
5
+ export { html, isBrowser };
6
+
7
- export const render = isBrowser ? litRender : renderToString;
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
- export const directive = isBrowser ? litDirective : litServerDirective;
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
- export const unsafeHTML = directive(value => part => {
45
+ ? directive((value) => (part) => {
12
- if (isBrowser) {
13
- if (!(part instanceof NodePart)) {
46
+ if (!(part instanceof NodePart)) {
14
- throw new Error('unsafeHTML can only be used in text bindings');
47
+ throw new Error('unsafeHTML can only be used in text bindings');
15
- }
48
+ }
16
- const previousValue = previousValues.get(part);
49
+ const previousValue = previousValues.get(part);
17
- if (previousValue !== undefined && isPrimitive(value) && value === previousValue.value && part.value === previousValue.fragment) {
50
+ if (previousValue !== undefined && isPrimitive(value) && value === previousValue.value && part.value === previousValue.fragment) {
18
- return;
51
+ return;
19
- }
52
+ }
20
- const template = document.createElement('template');
53
+ const template = document.createElement('template');
21
- template.innerHTML = value; // innerHTML casts to string internally
54
+ template.innerHTML = value; // innerHTML casts to string internally
22
- const fragment = document.importNode(template.content, true);
55
+ const fragment = document.importNode(template.content, true);
23
- part.setValue(fragment);
56
+ part.setValue(fragment);
24
- previousValues.set(part, { value, fragment });
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
- export const classMap = directive(classInfo => part => {
63
+ ? directive((classInfo) => (part) => {
35
- if (isBrowser) {
36
- if (!(part instanceof AttributePart) || part instanceof PropertyPart || part.committer.name !== 'class' || part.committer.parts.length > 1) {
64
+ if (!(part instanceof AttributePart) || part instanceof PropertyPart || part.committer.name !== 'class' || part.committer.parts.length > 1) {
37
- throw new Error('The `classMap` directive must be used in the `class` attribute ' + 'and must be the only part in the attribute.');
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
- // Add or remove classes based on their classMap value
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
- for (const name in classInfo) {
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
- if (typeof classList.commit === 'function') {
101
+ if (typeof classList.commit === 'function') {
74
- classList.commit();
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
- registry[name] = class extends AtomsElement {
436
+ clazz: class extends AtomsElement {
410
- constructor(attrs) {
437
+ constructor(attrs) {
411
- super();
438
+ super();
412
- this.name = name;
439
+ this.name = name;
413
- this.renderer = fn;
440
+ this.renderer = fn;
414
- this.attrTypes = attrTypes;
441
+ this.attrTypes = attrTypes;
415
- this.funcKeys = keys.filter(key => attrTypes[key].type === 'function');
442
+ this.funcKeys = keys.filter((key) => attrTypes[key].type === 'function');
416
- this.attrTypesMap = keys
443
+ this.attrTypesMap = keys
417
- .filter(key => attrTypes[key].type !== 'function')
444
+ .filter((key) => attrTypes[key].type !== 'function')
418
- .reduce((acc, key) => {
445
+ .reduce((acc, key) => {
419
- acc[key.toLowerCase()] = {
446
+ acc[key.toLowerCase()] = {
420
- propName: key,
447
+ propName: key,
421
- propType: attrTypes[key],
448
+ propType: attrTypes[key],
422
- };
449
+ };
423
- return acc;
450
+ return acc;
424
- }, {});
451
+ }, {});
425
- if (attrs) {
452
+ if (attrs) {
426
- attrs.forEach(item => {
453
+ attrs.forEach((item) => {
427
- this.attributeChangedCallback(item.name, null, item.value);
454
+ this.attributeChangedCallback(item.name, null, item.value);
428
- });
455
+ });
456
+ }
429
457
  }
430
- }
431
458
 
432
- static get observedAttributes() {
459
+ static get observedAttributes() {
433
- return keys;
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
- options !== undefined ? options.serializePropertyAttributes : false
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
- strings[0] === EMPTY_STRING_BUFFER ||
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
- : Buffer.concat([prefix, value]);
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
- string.indexOf(unsafePrefixString) === 0 ? string.slice(33) : escape(string, 'attribute')
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
- part.tagName === 'script' || part.tagName === 'style' ? part.tagName : 'text'
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
- [EMPTY_STRING_BUFFER$1, EMPTY_STRING_BUFFER$1],
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
- * @param { unknown } result - a template result returned from call to "html`...`"
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 { AttributePart, BooleanAttributePart, DefaultTemplateProcessor, DefaultTemplateResultProcessor, EventAttributePart, NodePart, Part, PropertyAttributePart, Template, TemplateResult, defaultTemplateProcessor, defaultTemplateResultProcessor, directive, html, isAttributePart, isDirective, isNodePart, isTemplateResult, nothing, renderToBuffer, renderToStream, renderToString, html as svg, templateCache, unsafePrefixString };
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 Clazz = getElement('app-item');
196
+ const { clazz } = getElement('app-item');
171
- const instance = new Clazz([{ name: 'address', value: JSON.stringify({ street: '123' }).replace(/"/g, `'`) }]);
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
- });