@esportsplus/template 0.12.11 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @esportsplus/template might be problematic. Click here for more details.

@@ -1,7 +1,9 @@
1
1
  import { Attributes, Element } from './types.js';
2
+ declare const apply: (element: Element) => void;
3
+ declare const spread: (element: Element, attributes: Attributes | Attributes[]) => void;
2
4
  declare const _default: {
3
5
  apply: (element: Element) => void;
4
- set: (element: Element, value: unknown, name: string) => void;
5
6
  spread: (element: Element, attributes: Attributes | Attributes[]) => void;
6
7
  };
7
8
  export default _default;
9
+ export { apply, spread };
@@ -1,11 +1,10 @@
1
1
  import { effect, root } from '@esportsplus/reactivity';
2
- import { ATTRIBUTES } from './constants.js';
3
2
  import { className, isArray, isObject, raf, removeAttribute, setAttribute } from './utilities.js';
4
3
  import event from './event.js';
5
4
  let attributes = {}, delimiters = {
6
5
  class: ' ',
7
6
  style: ';'
8
- };
7
+ }, key = Symbol();
9
8
  function attribute(element, name, value) {
10
9
  if (value === false || value == null) {
11
10
  value = '';
@@ -45,12 +44,22 @@ function reactive(element, id, name, value, wait = false) {
45
44
  }
46
45
  }
47
46
  function set(element, value, name) {
48
- if (typeof value === 'function') {
47
+ if (name === 'style' && isObject(value)) {
48
+ for (let key in value) {
49
+ set(element, value[key], name);
50
+ }
51
+ }
52
+ else if (isArray(value)) {
53
+ for (let i = 0, n = value.length; i < n; i++) {
54
+ set(element, value[i], name);
55
+ }
56
+ }
57
+ else if (typeof value === 'function') {
49
58
  if (name.startsWith('on')) {
50
59
  event(element, name, value);
51
60
  }
52
61
  else {
53
- reactive(element, ('e' + store(element)[ATTRIBUTES]++), name, value, true);
62
+ reactive(element, ('e' + store(element)[key]++), name, value, true);
54
63
  }
55
64
  }
56
65
  else {
@@ -58,7 +67,7 @@ function set(element, value, name) {
58
67
  }
59
68
  }
60
69
  function store(element) {
61
- return (element[ATTRIBUTES] || (element[ATTRIBUTES] = { [ATTRIBUTES]: 0 }));
70
+ return (element[key] || (element[key] = { [key]: 0 }));
62
71
  }
63
72
  function update(element, id, name, value, wait = false) {
64
73
  if (value === false || value == null) {
@@ -119,44 +128,32 @@ function update(element, id, name, value, wait = false) {
119
128
  attribute(element, name, value);
120
129
  }
121
130
  }
122
- export default {
123
- apply: (element) => {
124
- for (let key in attributes) {
125
- attribute(element, key, attributes[key]);
126
- }
127
- attributes = {};
128
- },
129
- set: (element, value, name) => {
130
- if (name === 'style' && isObject(value)) {
131
- for (let key in value) {
132
- set(element, value[key], name);
133
- }
134
- }
135
- else if (isArray(value)) {
136
- for (let i = 0, n = value.length; i < n; i++) {
137
- set(element, value[i], name);
138
- }
139
- }
140
- else {
141
- set(element, value, name);
131
+ const apply = (element) => {
132
+ for (let key in attributes) {
133
+ attribute(element, key, attributes[key]);
134
+ }
135
+ attributes = {};
136
+ };
137
+ const spread = function (element, attributes) {
138
+ if (isObject(attributes)) {
139
+ for (let name in attributes) {
140
+ set(element, attributes[name], name);
142
141
  }
143
- },
144
- spread: function (element, attributes) {
145
- if (isObject(attributes)) {
146
- for (let name in attributes) {
147
- this.set(element, attributes[name], name);
142
+ }
143
+ else if (isArray(attributes)) {
144
+ for (let i = 0, n = attributes.length; i < n; i++) {
145
+ let attrs = attributes[i];
146
+ if (!isObject(attrs)) {
147
+ continue;
148
148
  }
149
- }
150
- else if (isArray(attributes)) {
151
- for (let i = 0, n = attributes.length; i < n; i++) {
152
- let attrs = attributes[i];
153
- if (!isObject(attrs)) {
154
- continue;
155
- }
156
- for (let name in attrs) {
157
- this.set(element, attrs[name], name);
158
- }
149
+ for (let name in attrs) {
150
+ set(element, attrs[name], name);
159
151
  }
160
152
  }
161
153
  }
154
+ else {
155
+ throw new Error('@esportsplus/template: attributes must be of type `Attributes` or `Attributes[]`; Received ' + JSON.stringify(attributes));
156
+ }
162
157
  };
158
+ export default { apply, spread };
159
+ export { apply, spread };
@@ -1,13 +1,9 @@
1
- declare const ATTRIBUTES: unique symbol;
2
1
  declare const NODE_CLOSING = 1;
3
2
  declare const NODE_ELEMENT = 3;
4
3
  declare const NODE_SLOT = 4;
5
4
  declare const NODE_VOID = 5;
6
5
  declare const NODE_WHITELIST: Record<string, number>;
7
6
  declare const REGEX_EMPTY_TEXT_NODES: RegExp;
8
- declare const REGEX_EVENTS: RegExp;
9
- declare const REGEX_EXTRA_WHITESPACE: RegExp;
10
- declare const REGEX_SLOT_ATTRIBUTES: RegExp;
11
7
  declare const REGEX_SLOT_NODES: RegExp;
12
8
  declare const RENDERABLE: unique symbol;
13
9
  declare const RENDERABLE_REACTIVE: unique symbol;
@@ -15,4 +11,5 @@ declare const RENDERABLE_TEMPLATE: unique symbol;
15
11
  declare const SLOT: unique symbol;
16
12
  declare const SLOT_HTML = "<!--$-->";
17
13
  declare const SLOT_MARKER = "{{$}}";
18
- export { ATTRIBUTES, NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_EVENTS, REGEX_EXTRA_WHITESPACE, REGEX_SLOT_ATTRIBUTES, REGEX_SLOT_NODES, RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE, SLOT, SLOT_HTML, SLOT_MARKER };
14
+ declare const SLOT_MARKER_LENGTH: number;
15
+ export { NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES, RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE, SLOT, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH };
@@ -1,4 +1,3 @@
1
- const ATTRIBUTES = Symbol();
2
1
  const NODE_CLOSING = 1;
3
2
  const NODE_COMMENT = 2;
4
3
  const NODE_ELEMENT = 3;
@@ -25,9 +24,6 @@ const NODE_WHITELIST = {
25
24
  'wbr': NODE_VOID
26
25
  };
27
26
  const REGEX_EMPTY_TEXT_NODES = /(>|})\s+(<|{)/g;
28
- const REGEX_EVENTS = /(?:\s*on[\w-:]+\s*=\s*["'][^"']*["'])/g;
29
- const REGEX_EXTRA_WHITESPACE = /\s\s+/g;
30
- const REGEX_SLOT_ATTRIBUTES = /<[\w-]+([^><]*{{\$}}[^><]*)>/g;
31
27
  const REGEX_SLOT_NODES = /<([\w-]+|[\/!])(?:([^><]*{{\$}}[^><]*)|(?:[^><]*))?>|{{\$}}/g;
32
28
  const RENDERABLE = Symbol();
33
29
  const RENDERABLE_REACTIVE = Symbol();
@@ -35,4 +31,5 @@ const RENDERABLE_TEMPLATE = Symbol();
35
31
  const SLOT = Symbol();
36
32
  const SLOT_HTML = '<!--$-->';
37
33
  const SLOT_MARKER = '{{$}}';
38
- export { ATTRIBUTES, NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_EVENTS, REGEX_EXTRA_WHITESPACE, REGEX_SLOT_ATTRIBUTES, REGEX_SLOT_NODES, RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE, SLOT, SLOT_HTML, SLOT_MARKER };
34
+ const SLOT_MARKER_LENGTH = SLOT_MARKER.length;
35
+ export { NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES, RENDERABLE, RENDERABLE_REACTIVE, RENDERABLE_TEMPLATE, SLOT, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH };
@@ -1,67 +1,19 @@
1
- import { NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_EVENTS, REGEX_EXTRA_WHITESPACE, REGEX_SLOT_ATTRIBUTES, REGEX_SLOT_NODES, SLOT_HTML, SLOT_MARKER } from '../constants.js';
1
+ import { NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_SLOT_NODES, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH } from '../constants.js';
2
2
  import { firstChild, firstElementChild, fragment, nextElementSibling, nextSibling } from '../utilities.js';
3
- import a from '../attributes.js';
3
+ import { spread } from '../attributes.js';
4
4
  import s from '../slot.js';
5
5
  let cache = new WeakMap();
6
6
  function build(literals, values) {
7
7
  if (values.length === 0) {
8
8
  return set(literals, literals[0]);
9
9
  }
10
- let attributes = {}, buffer = '', html = minify(literals.join(SLOT_MARKER)), index = 0, level = 0, levels = [{
10
+ let buffer = '', html = literals.join(SLOT_MARKER)
11
+ .replace(REGEX_EMPTY_TEXT_NODES, '$1$2')
12
+ .trim(), index = 0, level = 0, levels = [{
11
13
  children: 0,
12
14
  elements: 0,
13
15
  path: []
14
- }], slot = 0, slots = [], total = values.length;
15
- {
16
- let attribute = '', buffer = '', char = '', quote = '';
17
- for (let match of html.matchAll(REGEX_SLOT_ATTRIBUTES)) {
18
- let found = match[1], metadata = attributes[found];
19
- if (metadata) {
20
- continue;
21
- }
22
- metadata = attributes[found] = [];
23
- for (let i = 0, n = found.length; i < n; i++) {
24
- char = found[i];
25
- if (char === ' ') {
26
- buffer = '';
27
- }
28
- else if (char === '=') {
29
- attribute = buffer;
30
- buffer = '';
31
- }
32
- else if (char === '"' || char === "'") {
33
- if (!attribute) {
34
- }
35
- else if (!quote) {
36
- quote = char;
37
- }
38
- else if (quote === char) {
39
- attribute = '';
40
- buffer = '';
41
- quote = '';
42
- }
43
- }
44
- else if (char === '{' && char !== buffer) {
45
- buffer = char;
46
- }
47
- else {
48
- buffer += char;
49
- if (buffer === SLOT_MARKER) {
50
- buffer = '';
51
- if (attribute) {
52
- metadata.push(attribute);
53
- if (!quote) {
54
- attribute = '';
55
- }
56
- }
57
- else {
58
- metadata.push(null);
59
- }
60
- }
61
- }
62
- }
63
- }
64
- }
16
+ }], parsed = html.split(SLOT_MARKER), slot = 0, slots = [], total = values.length;
65
17
  for (let match of html.matchAll(REGEX_SLOT_NODES)) {
66
18
  let parent = levels[level], type = match[1] === undefined ? NODE_SLOT : (NODE_WHITELIST[match[1].toLowerCase()] || NODE_ELEMENT);
67
19
  if ((match.index || 1) - 1 > index) {
@@ -70,19 +22,15 @@ function build(literals, values) {
70
22
  if (type === NODE_ELEMENT || type === NODE_VOID) {
71
23
  let attr = match[2];
72
24
  if (attr) {
73
- let metadata = attributes[attr], path = methods(parent.children, parent.path, firstChild, nextSibling);
74
- if (!metadata) {
75
- throw new Error(`Template: attribute metadata could not be found for '${attr}'`);
76
- }
77
- for (let i = 0, n = metadata.length; i < n; i++) {
78
- let name = metadata[i];
25
+ let i = attr.indexOf(SLOT_MARKER), path = methods(parent.children, parent.path, firstChild, nextSibling);
26
+ while (i !== -1) {
79
27
  slots.push({
80
- fn: name === null ? a.spread : a.set,
81
- name,
28
+ fn: spread,
82
29
  path,
83
30
  slot
84
31
  });
85
- buffer += literals[slot++];
32
+ buffer += parsed[slot++];
33
+ i = attr.indexOf(SLOT_MARKER, i + SLOT_MARKER_LENGTH);
86
34
  }
87
35
  }
88
36
  if (type === NODE_ELEMENT) {
@@ -97,16 +45,15 @@ function build(literals, values) {
97
45
  parent.elements++;
98
46
  }
99
47
  else if (type === NODE_SLOT) {
100
- buffer += literals[slot] + SLOT_HTML;
48
+ buffer += parsed[slot] + SLOT_HTML;
101
49
  slots.push({
102
50
  fn: s,
103
- name: null,
104
51
  path: methods(parent.children, parent.path, firstChild, nextSibling),
105
52
  slot: slot++
106
53
  });
107
54
  }
108
55
  if (slot === total) {
109
- buffer += literals[slot];
56
+ buffer += parsed[slot];
110
57
  break;
111
58
  }
112
59
  if (type === NODE_CLOSING) {
@@ -117,7 +64,7 @@ function build(literals, values) {
117
64
  }
118
65
  index = (match.index || 0) + match[0].length;
119
66
  }
120
- return set(literals, minify(buffer.replace(REGEX_EVENTS, '')), slots);
67
+ return set(literals, buffer, slots);
121
68
  }
122
69
  function methods(children, copy, first, next) {
123
70
  let methods = copy.slice();
@@ -127,9 +74,6 @@ function methods(children, copy, first, next) {
127
74
  }
128
75
  return methods;
129
76
  }
130
- function minify(html) {
131
- return html.replace(REGEX_EMPTY_TEXT_NODES, '$1$2').replace(REGEX_EXTRA_WHITESPACE, ' ').trim();
132
- }
133
77
  function set(literals, html, slots = null) {
134
78
  let template = {
135
79
  fragment: fragment(html),
@@ -1,6 +1,6 @@
1
1
  import { root } from '@esportsplus/reactivity';
2
2
  import { cloneNode, firstChild, nextSibling } from '../utilities.js';
3
- import a from '../attributes.js';
3
+ import { apply } from '../attributes.js';
4
4
  import cache from './cache.js';
5
5
  function reactive(renderable, slot) {
6
6
  let array = renderable.values, factory = renderable.template, refresh = () => {
@@ -23,10 +23,10 @@ function render(renderable, template) {
23
23
  if (slots !== null) {
24
24
  let node, previous, values = renderable.values;
25
25
  for (let i = slots.length - 1; i >= 0; i--) {
26
- let { fn, name, path, slot } = slots[i];
26
+ let { fn, path, slot } = slots[i];
27
27
  if (path === previous) { }
28
28
  else {
29
- a.apply(node);
29
+ apply(node);
30
30
  node = fragment;
31
31
  previous = path;
32
32
  for (let o = 0, j = path.length; o < j; o++) {
@@ -35,7 +35,9 @@ function render(renderable, template) {
35
35
  }
36
36
  fn(node, values[slot], name);
37
37
  }
38
- a.apply(node);
38
+ if (node) {
39
+ apply(node);
40
+ }
39
41
  }
40
42
  for (let element = firstChild.call(fragment); element; element = nextSibling.call(element)) {
41
43
  elements.push(element);
package/build/types.d.ts CHANGED
@@ -12,6 +12,8 @@ type Attributes = {
12
12
  } & {
13
13
  [key: `aria-${string}`]: string | number | boolean | undefined;
14
14
  [key: `data-${string}`]: string | undefined;
15
+ onmount?: (element: Element) => void;
16
+ onrender?: (element: Element) => void;
15
17
  } & Record<PropertyKey, unknown>;
16
18
  type Effect<T> = () => EffectResponse<T>;
17
19
  type EffectResponse<T> = T extends [] ? EffectResponse<T[number]>[] : Primitive | Renderable<T>;
@@ -37,8 +39,7 @@ type Template = {
37
39
  html: string;
38
40
  literals: TemplateStringsArray;
39
41
  slots: {
40
- fn: typeof attributes.set | typeof attributes.spread | typeof event | typeof slot;
41
- name: PropertyKey | null;
42
+ fn: typeof attributes.spread | typeof event | typeof slot;
42
43
  path: typeof firstChild[];
43
44
  slot: number;
44
45
  }[] | null;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "dependencies": {
4
4
  "@esportsplus/reactivity": "^0.4.6",
5
5
  "@esportsplus/tasks": "^0.1.9",
6
- "@esportsplus/utilities": "^0.17.3"
6
+ "@esportsplus/utilities": "^0.19.0"
7
7
  },
8
8
  "devDependencies": {
9
9
  "@esportsplus/typescript": "^0.9.1"
@@ -13,7 +13,7 @@
13
13
  "private": false,
14
14
  "type": "module",
15
15
  "types": "./build/index.d.ts",
16
- "version": "0.12.11",
16
+ "version": "0.13.0",
17
17
  "scripts": {
18
18
  "build": "tsc && tsc-alias",
19
19
  "-": "-"
package/src/attributes.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { effect, root } from '@esportsplus/reactivity';
2
- import { ATTRIBUTES } from './constants';
3
2
  import { Attributes, Element } from './types';
4
3
  import { className, isArray, isObject, raf, removeAttribute, setAttribute } from './utilities';
5
4
  import event from './event';
@@ -9,7 +8,8 @@ let attributes: Record<string, unknown> = {},
9
8
  delimiters: Record<string, string> = {
10
9
  class: ' ',
11
10
  style: ';'
12
- };
11
+ },
12
+ key = Symbol();
13
13
 
14
14
 
15
15
  function attribute(element: Element, name: string, value: unknown) {
@@ -55,12 +55,22 @@ function reactive(element: Element, id: string, name: string, value: unknown, wa
55
55
  }
56
56
 
57
57
  function set(element: Element, value: unknown, name: string) {
58
- if (typeof value === 'function') {
58
+ if (name === 'style' && isObject(value)) {
59
+ for (let key in value) {
60
+ set(element, value[key], name);
61
+ }
62
+ }
63
+ else if (isArray(value)) {
64
+ for (let i = 0, n = value.length; i < n; i++) {
65
+ set(element, value[i], name);
66
+ }
67
+ }
68
+ else if (typeof value === 'function') {
59
69
  if (name.startsWith('on')) {
60
70
  event(element, name, value);
61
71
  }
62
72
  else {
63
- reactive(element, ('e' + store(element)[ATTRIBUTES]++), name, value, true);
73
+ reactive(element, ('e' + store(element)[key]++), name, value, true);
64
74
  }
65
75
  }
66
76
  else {
@@ -70,8 +80,8 @@ function set(element: Element, value: unknown, name: string) {
70
80
 
71
81
  function store(element: Element) {
72
82
  return (
73
- element[ATTRIBUTES] || (element[ATTRIBUTES] = { [ATTRIBUTES]: 0 })
74
- ) as Attributes & { [ATTRIBUTES]: number };
83
+ element[key] || (element[key] = { [key]: 0 })
84
+ ) as Attributes & { [key]: number };
75
85
  }
76
86
 
77
87
  function update(element: Element, id: null | string, name: string, value: unknown, wait = false) {
@@ -155,47 +165,37 @@ function update(element: Element, id: null | string, name: string, value: unknow
155
165
  }
156
166
 
157
167
 
158
- export default {
159
- apply: (element: Element) => {
160
- for (let key in attributes) {
161
- attribute(element, key, attributes[key]);
162
- }
168
+ const apply = (element: Element) => {
169
+ for (let key in attributes) {
170
+ attribute(element, key, attributes[key]);
171
+ }
163
172
 
164
- attributes = {};
165
- },
166
- set: (element: Element, value: unknown, name: string) => {
167
- if (name === 'style' && isObject(value)) {
168
- for (let key in value) {
169
- set(element, value[key], name);
170
- }
171
- }
172
- else if (isArray(value)) {
173
- for (let i = 0, n = value.length; i < n; i++) {
174
- set(element, value[i], name);
175
- }
176
- }
177
- else {
178
- set(element, value, name);
179
- }
180
- },
181
- spread: function (element: Element, attributes: Attributes | Attributes[]) {
182
- if (isObject(attributes)) {
183
- for (let name in attributes) {
184
- this.set(element, attributes[name], name);
185
- }
173
+ attributes = {};
174
+ };
175
+
176
+ const spread = function (element: Element, attributes: Attributes | Attributes[]) {
177
+ if (isObject(attributes)) {
178
+ for (let name in attributes) {
179
+ set(element, attributes[name], name);
186
180
  }
187
- else if (isArray(attributes)) {
188
- for (let i = 0, n = attributes.length; i < n; i++) {
189
- let attrs = attributes[i];
181
+ }
182
+ else if (isArray(attributes)) {
183
+ for (let i = 0, n = attributes.length; i < n; i++) {
184
+ let attrs = attributes[i];
190
185
 
191
- if (!isObject(attrs)) {
192
- continue;
193
- }
186
+ if (!isObject(attrs)) {
187
+ continue;
188
+ }
194
189
 
195
- for (let name in attrs) {
196
- this.set(element, attrs[name], name);
197
- }
190
+ for (let name in attrs) {
191
+ set(element, attrs[name], name);
198
192
  }
199
193
  }
200
194
  }
201
- };
195
+ else {
196
+ throw new Error('@esportsplus/template: attributes must be of type `Attributes` or `Attributes[]`; Received ' + JSON.stringify(attributes));
197
+ }
198
+ };
199
+
200
+ export default { apply, spread };
201
+ export { apply, spread }
package/src/constants.ts CHANGED
@@ -1,6 +1,3 @@
1
- const ATTRIBUTES = Symbol();
2
-
3
-
4
1
  const NODE_CLOSING = 1;
5
2
 
6
3
  const NODE_COMMENT = 2;
@@ -36,12 +33,6 @@ const NODE_WHITELIST: Record<string, number> = {
36
33
 
37
34
  const REGEX_EMPTY_TEXT_NODES = /(>|})\s+(<|{)/g;
38
35
 
39
- const REGEX_EVENTS = /(?:\s*on[\w-:]+\s*=\s*["'][^"']*["'])/g;
40
-
41
- const REGEX_EXTRA_WHITESPACE = /\s\s+/g;
42
-
43
- const REGEX_SLOT_ATTRIBUTES = /<[\w-]+([^><]*{{\$}}[^><]*)>/g;
44
-
45
36
  const REGEX_SLOT_NODES = /<([\w-]+|[\/!])(?:([^><]*{{\$}}[^><]*)|(?:[^><]*))?>|{{\$}}/g;
46
37
 
47
38
 
@@ -58,23 +49,22 @@ const SLOT_HTML = '<!--$-->';
58
49
 
59
50
  const SLOT_MARKER = '{{$}}';
60
51
 
52
+ const SLOT_MARKER_LENGTH = SLOT_MARKER.length;
53
+
61
54
 
62
55
  export {
63
- ATTRIBUTES,
64
56
  NODE_CLOSING,
65
57
  NODE_ELEMENT,
66
58
  NODE_SLOT,
67
59
  NODE_VOID,
68
60
  NODE_WHITELIST,
69
61
  REGEX_EMPTY_TEXT_NODES,
70
- REGEX_EVENTS,
71
- REGEX_EXTRA_WHITESPACE,
72
- REGEX_SLOT_ATTRIBUTES,
73
62
  REGEX_SLOT_NODES,
74
63
  RENDERABLE,
75
64
  RENDERABLE_REACTIVE,
76
65
  RENDERABLE_TEMPLATE,
77
66
  SLOT,
78
67
  SLOT_HTML,
79
- SLOT_MARKER
68
+ SLOT_MARKER,
69
+ SLOT_MARKER_LENGTH
80
70
  };
package/src/html/cache.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
- NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES, REGEX_EVENTS, REGEX_EXTRA_WHITESPACE,
3
- REGEX_SLOT_ATTRIBUTES, REGEX_SLOT_NODES, SLOT_HTML, SLOT_MARKER
2
+ NODE_CLOSING, NODE_ELEMENT, NODE_SLOT, NODE_VOID, NODE_WHITELIST, REGEX_EMPTY_TEXT_NODES,
3
+ REGEX_SLOT_NODES, SLOT_HTML, SLOT_MARKER, SLOT_MARKER_LENGTH
4
4
  } from '~/constants';
5
5
  import { RenderableTemplate, Template } from '~/types';
6
6
  import { firstChild, firstElementChild, fragment, nextElementSibling, nextSibling } from '~/utilities';
7
- import a from '~/attributes';
7
+ import { spread } from '~/attributes';
8
8
  import s from '~/slot';
9
9
 
10
10
 
@@ -16,9 +16,10 @@ function build(literals: TemplateStringsArray, values: unknown[]) {
16
16
  return set(literals, literals[0]);
17
17
  }
18
18
 
19
- let attributes: Record<string, (null | string)[]> = {},
20
- buffer = '',
21
- html = minify(literals.join(SLOT_MARKER)),
19
+ let buffer = '',
20
+ html = literals.join(SLOT_MARKER)
21
+ .replace(REGEX_EMPTY_TEXT_NODES, '$1$2')
22
+ .trim(),
22
23
  index = 0,
23
24
  level = 0,
24
25
  levels = [{
@@ -26,73 +27,11 @@ function build(literals: TemplateStringsArray, values: unknown[]) {
26
27
  elements: 0,
27
28
  path: [] as NonNullable<Template['slots']>[0]['path']
28
29
  }],
30
+ parsed = html.split(SLOT_MARKER),
29
31
  slot = 0,
30
32
  slots: Template['slots'] = [],
31
33
  total = values.length;
32
34
 
33
- {
34
- let attribute = '',
35
- buffer = '',
36
- char = '',
37
- quote = '';
38
-
39
- for (let match of html.matchAll(REGEX_SLOT_ATTRIBUTES)) {
40
- let found = match[1],
41
- metadata = attributes[found];
42
-
43
- if (metadata) {
44
- continue;
45
- }
46
-
47
- metadata = attributes[found] = [];
48
-
49
- for (let i = 0, n = found.length; i < n; i++) {
50
- char = found[i];
51
-
52
- if (char === ' ') {
53
- buffer = '';
54
- }
55
- else if (char === '=') {
56
- attribute = buffer;
57
- buffer = '';
58
- }
59
- else if (char === '"' || char === "'") {
60
- if (!attribute) {
61
- }
62
- else if (!quote) {
63
- quote = char;
64
- }
65
- else if (quote === char) {
66
- attribute = '';
67
- buffer = '';
68
- quote = '';
69
- }
70
- }
71
- else if (char === '{' && char !== buffer) {
72
- buffer = char;
73
- }
74
- else {
75
- buffer += char;
76
-
77
- if (buffer === SLOT_MARKER) {
78
- buffer = '';
79
-
80
- if (attribute) {
81
- metadata.push(attribute);
82
-
83
- if (!quote) {
84
- attribute = '';
85
- }
86
- }
87
- else {
88
- metadata.push(null);
89
- }
90
- }
91
- }
92
- }
93
- }
94
- }
95
-
96
35
  for (let match of html.matchAll(REGEX_SLOT_NODES)) {
97
36
  let parent = levels[level],
98
37
  type = match[1] === undefined ? NODE_SLOT : (NODE_WHITELIST[match[1].toLowerCase()] || NODE_ELEMENT);
@@ -106,24 +45,18 @@ function build(literals: TemplateStringsArray, values: unknown[]) {
106
45
  let attr = match[2];
107
46
 
108
47
  if (attr) {
109
- let metadata = attributes[attr],
48
+ let i = attr.indexOf(SLOT_MARKER),
110
49
  path = methods(parent.children, parent.path, firstChild, nextSibling);
111
50
 
112
- if (!metadata) {
113
- throw new Error(`Template: attribute metadata could not be found for '${attr}'`);
114
- }
115
-
116
- for (let i = 0, n = metadata.length; i < n; i++) {
117
- let name = metadata[i];
118
-
51
+ while (i !== -1) {
119
52
  slots.push({
120
- fn: name === null ? a.spread : a.set,
121
- name,
53
+ fn: spread,
122
54
  path,
123
55
  slot
124
56
  });
125
57
 
126
- buffer += literals[slot++];
58
+ buffer += parsed[slot++];
59
+ i = attr.indexOf(SLOT_MARKER, i + SLOT_MARKER_LENGTH);
127
60
  }
128
61
  }
129
62
 
@@ -140,17 +73,16 @@ function build(literals: TemplateStringsArray, values: unknown[]) {
140
73
  parent.elements++;
141
74
  }
142
75
  else if (type === NODE_SLOT) {
143
- buffer += literals[slot] + SLOT_HTML;
76
+ buffer += parsed[slot] + SLOT_HTML;
144
77
  slots.push({
145
78
  fn: s,
146
- name: null,
147
79
  path: methods(parent.children, parent.path, firstChild, nextSibling),
148
80
  slot: slot++
149
81
  });
150
82
  }
151
83
 
152
84
  if (slot === total) {
153
- buffer += literals[slot];
85
+ buffer += parsed[slot];
154
86
  break;
155
87
  }
156
88
 
@@ -164,7 +96,7 @@ function build(literals: TemplateStringsArray, values: unknown[]) {
164
96
  index = (match.index || 0) + match[0].length;
165
97
  }
166
98
 
167
- return set(literals, minify(buffer.replace(REGEX_EVENTS, '')), slots);
99
+ return set(literals, buffer, slots);
168
100
  }
169
101
 
170
102
  function methods(children: number, copy: (typeof firstChild)[], first: (typeof firstChild), next: (typeof firstChild)) {
@@ -179,10 +111,6 @@ function methods(children: number, copy: (typeof firstChild)[], first: (typeof f
179
111
  return methods;
180
112
  }
181
113
 
182
- function minify(html: string) {
183
- return html.replace(REGEX_EMPTY_TEXT_NODES, '$1$2').replace(REGEX_EXTRA_WHITESPACE, ' ').trim();
184
- }
185
-
186
114
  function set(literals: TemplateStringsArray, html: string, slots: Template['slots'] = null) {
187
115
  let template = {
188
116
  fragment: fragment(html),
@@ -2,7 +2,7 @@ import { root, ReactiveArray } from '@esportsplus/reactivity';
2
2
  import { Element, Elements, Renderable, RenderableReactive, RenderableTemplate, Template } from '~/types';
3
3
  import { Slot } from '~/slot';
4
4
  import { cloneNode, firstChild, nextSibling } from '~/utilities';
5
- import a from '~/attributes';
5
+ import { apply } from '~/attributes';
6
6
  import cache from './cache';
7
7
 
8
8
 
@@ -41,11 +41,11 @@ function render<T>(renderable: Renderable<T>, template: Template) {
41
41
  values = renderable.values;
42
42
 
43
43
  for (let i = slots.length - 1; i >= 0; i--) {
44
- let { fn, name, path, slot } = slots[i];
44
+ let { fn, path, slot } = slots[i];
45
45
 
46
46
  if (path === previous) {}
47
47
  else {
48
- a.apply(node);
48
+ apply(node);
49
49
 
50
50
  node = fragment;
51
51
  previous = path;
@@ -59,7 +59,9 @@ function render<T>(renderable: Renderable<T>, template: Template) {
59
59
  fn(node, values[slot], name);
60
60
  }
61
61
 
62
- a.apply(node);
62
+ if (node) {
63
+ apply(node);
64
+ }
63
65
  }
64
66
 
65
67
  for (let element = firstChild.call(fragment as Element); element; element = nextSibling.call(element)) {
package/src/slot.ts CHANGED
@@ -5,8 +5,8 @@ import { Element, Elements, RenderableReactive, RenderableTemplate } from './typ
5
5
  import { firstChild, isArray, isObject, nextSibling, nodeValue, raf, text } from './utilities'
6
6
 
7
7
 
8
- // Using a private symbol since 'SLOT' is used as a different flag in 'render.ts'
9
8
  let cleanup: Slot[] = [],
9
+ // Using a private symbol since 'SLOT' is used as a different flag in 'render.ts'
10
10
  key = Symbol(),
11
11
  scheduled = false;
12
12
 
package/src/types.ts CHANGED
@@ -14,6 +14,12 @@ type Attributes = {
14
14
  } & {
15
15
  [key: `aria-${string}`]: string | number | boolean | undefined;
16
16
  [key: `data-${string}`]: string | undefined;
17
+ // Element is mounted to DOM
18
+ // - Trigger animations, etc.
19
+ onmount?: (element: Element) => void;
20
+ // Element is rendered in fragment
21
+ // - Used to retrieve reference to the element
22
+ onrender?: (element: Element) => void;
17
23
  } & Record<PropertyKey, unknown>;
18
24
 
19
25
  type Effect<T> = () => EffectResponse<T>;
@@ -51,8 +57,7 @@ type Template = {
51
57
  html: string;
52
58
  literals: TemplateStringsArray;
53
59
  slots: {
54
- fn: typeof attributes.set | typeof attributes.spread | typeof event | typeof slot;
55
- name: PropertyKey | null;
60
+ fn: typeof attributes.spread | typeof event | typeof slot;
56
61
  path: typeof firstChild[];
57
62
  slot: number;
58
63
  }[] | null;