solarite 0.5.2 → 0.7.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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/dist/Solarite-debug.js +2823 -1856
  3. package/dist/Solarite.js +2779 -1824
  4. package/dist/Solarite.min.js +2 -4
  5. package/package.json +16 -3
  6. package/readme.md +58 -11
  7. package/src/Globals.js +54 -72
  8. package/src/HtmlParser.js +90 -90
  9. package/src/MultiValueMap.js +57 -105
  10. package/src/NodeGroup.js +624 -470
  11. package/src/Path.js +224 -211
  12. package/src/PathToAttribValue.js +401 -261
  13. package/src/PathToAttribs.js +113 -80
  14. package/src/PathToComment.js +7 -7
  15. package/src/PathToComponent.js +183 -188
  16. package/src/PathToEvent.js +76 -64
  17. package/src/PathToKey.js +19 -0
  18. package/src/PathToNodes.js +1053 -566
  19. package/src/RootNodeGroup.js +120 -8
  20. package/src/Shell.js +570 -348
  21. package/src/Solarite.d.ts +134 -113
  22. package/src/Solarite.js +243 -286
  23. package/src/Template.js +195 -274
  24. package/src/Util.js +353 -351
  25. package/src/assert.js +10 -10
  26. package/src/assignAttributes.js +63 -0
  27. package/src/delve.js +55 -43
  28. package/src/h.js +220 -138
  29. package/src/jsx-dev-runtime.d.ts +1 -0
  30. package/src/jsx-dev-runtime.js +5 -0
  31. package/src/jsx-runtime.d.ts +21 -0
  32. package/src/jsx-runtime.js +84 -0
  33. package/src/jsx.js +194 -0
  34. package/src/toEl.js +77 -82
  35. package/dist/udomdiff-license.txt +0 -18
  36. package/src/getArg.js +0 -137
  37. package/src/hash.js +0 -89
  38. package/src/udomdiff.js +0 -176
  39. package/src/unused/FastLookupArray.js +0 -54
  40. package/src/unused/Hashes.js +0 -339
  41. package/src/unused/InUse.test.js +0 -92
  42. package/src/unused/InUseMap.js +0 -98
  43. package/src/unused/LinkedList.js +0 -117
  44. package/src/unused/LinkedList.test.js +0 -115
  45. package/src/unused/Misc.js +0 -13
  46. package/src/unused/Perf.js +0 -47
  47. package/src/unused/TrackedArray.js +0 -54
  48. package/src/unused/WeakArray.js +0 -33
  49. package/src/watch.js +0 -543
package/src/Solarite.js CHANGED
@@ -1,286 +1,243 @@
1
- /*
2
- ┏┓ ┓ •
3
- ┗┓┏┓┃┏┓┏┓┓╋▗▖
4
- ┗┛┗┛┗┗┻╹ ╹╹┗
5
- JavasCript UI library
6
- @license MIT
7
- @copyright Vorticode LLC
8
- https://vorticode.github.io/solarite/ */
9
- import h from './h.js';
10
- export default h;
11
- export {default as delve} from './delve.js';
12
- export {default as Template} from './Template.js';
13
- export {default as toEl} from './toEl.js';
14
- import Template from './Template.js';
15
-
16
- // Experimental:
17
- //--------------
18
- export {default as Globals} from './Globals.js';
19
- export {default as SolariteUtil} from './Util.js';
20
-
21
- // Deprecated:
22
- //--------------
23
- export {default as h} from './h.js'; // Named exports for h() are deprecated.
24
- export {getArg, ArgType} from './getArg.js';
25
- export {setArgs} from './getArg.js';
26
- export {default as r} from './h.js';
27
- export function t(html) {
28
- return new Template([html], []);
29
- }
30
- //export {default as watch, renderWatched} from './watch.js'; // unfinished
31
-
32
- // Only used for tests:
33
- //--------------
34
- export {default as HtmlParser} from './HtmlParser.js';
35
- export {default as NodeGroup} from './NodeGroup.js';
36
- export {default as Shell} from './Shell.js';
37
-
38
-
39
-
40
-
41
-
42
- // Solarite Class:
43
- //--------------
44
- import Util from "./Util.js";
45
- import Globals from "./Globals.js";
46
-
47
- /**
48
- * Intercept the construct call to auto-define the class before the constructor is called. */
49
- let HTMLElementAutoDefine = new Proxy(HTMLElement, {
50
- construct(Parent, args, Class) {
51
-
52
- // 1. Call customElements.define() automatically.
53
- Util.defineClass(Class);
54
-
55
- // 2. This line is equivalent the to super() call to HTMLElement:
56
- return Reflect.construct(Parent, args, Class);
57
- }
58
- });
59
-
60
- /**
61
- * Solarite provides more features if your web component extends Solarite instead of HTMLElement.
62
- *
63
- * Reasons to inherit from Solarite instead of HTMLElement.
64
- * 1. customElements.define() is called automatically when you create the first instance.
65
- * 2. Calls render() when added to the DOM, if it hasn't been called already.
66
- * 3. Populates the attribs argument to the constructor when instantiated from regular html outside a template string.
67
- * It parses JSON from DOM attribute values surrouned with '${...}'
68
- * 4. Shows an error if render() isn't defined.
69
- *
70
- * Advantages to inheriting from HTMLElement
71
- * 1. We can inherit from things like HTMLTableRowElement directly.
72
- * 2. There's less magic, since everyone is familiar with defining custom elements.
73
- * 3. No confusion about how the class name becomes a tag name.
74
- * @extends {HTMLElement} */
75
- export class Solarite extends HTMLElementAutoDefine {
76
-
77
- /**
78
- * @param attribs {?Record<string, any>} */
79
- constructor(attribs=null) {
80
- super();
81
-
82
- if (attribs) {
83
- if (typeof attribs !== 'object')
84
- throw new Error('First argument to custom element constructor must be an object.');
85
-
86
- // 1. Populate attribs if it's an empty object.
87
- if (attribs && !Object.keys(attribs).length) {
88
- let attribs2 = Solarite.getAttribs(this);
89
- for (let name in attribs2) {
90
- attribs[name] = attribs2[name];
91
- }
92
- }
93
-
94
- // 2. Populate fields from attribs.
95
- // This does nothing because the fields are overwritten by the child class after this super() constructor executes.
96
- //for (let name in attribs || {}) {
97
- // if (name in this) {
98
- // const descriptor = Object.getOwnPropertyDescriptor(this, name);
99
- // if (!descriptor || descriptor.writable || descriptor.set)
100
- // this[name] = attribs[name];
101
- // }
102
- //}
103
- }
104
-
105
- // 3. Wrap render function so it always provides the attribs argument.
106
- // Disabled because this gives us strings for attribute values when we call render manually.
107
- // Instead of values given from ${...} expressions.
108
- // let originalRender = this.render;
109
- // this.render = (attribs, changed=true) => {
110
- // if (!attribs) // If we have to look up the attribs, we don't know if they changed or not.
111
- // attribs = Solarite.getAttribs(this);
112
- // originalRender.call(this, attribs, changed);
113
- // }
114
- }
115
-
116
- 'render'() {
117
- throw new Error('render() is not defined for ' + this.constructor.name);
118
- }
119
-
120
- /**
121
- * Call render() only if it hasn't already been called. */
122
- 'renderFirstTime'() {
123
- if (!Globals.rendered.has(this)) {
124
- let attribs = Solarite.getAttribs(this);
125
- this.render(attribs); // calls Globals.rendered.add(this); inside the call to h()'...'.
126
- }
127
- }
128
-
129
- /**
130
- * Called automatically by the browser. */
131
- 'connectedCallback'() { // quoted so terser doesn't remove it.
132
- this.renderFirstTime();
133
- }
134
-
135
- static 'define'(tagName=null) {
136
- Util.defineClass(this, tagName);
137
- }
138
-
139
- static 'getAttribs'(el) {
140
- let result = Util.attribsToObject(el);
141
- for (let name in result) {
142
- let val = result[name];
143
- if (val.startsWith('${') && val.endsWith('}'))
144
- result[name] = JSON.parse(val.slice(2, -1));
145
- }
146
- return result;
147
- }
148
-
149
-
150
- // TODO: Do we want to use this to get the tag name from the render() function, instead of having the user define it?
151
- /**
152
- * Get the tag name for a class, as defined by the tag used in render().
153
- *
154
- * This will parse the JavaScript code of the render() function to find the tag name.
155
- * It will itarage every character, keeping track of quotes and comments so it can
156
- * skip them until it finds the tag name passed to h(this)`<tagname>` inside the render() function.
157
- *
158
- * */
159
- /*
160
- static getTagName(Class) {
161
- let code = Class.prototype.render.toString();
162
- let i = 0;
163
- while (i < code.length) {
164
- let char = code[i];
165
- let next = code[i + 1];
166
-
167
- // Skip single line comments
168
- if (char === '/' && next === '/') {
169
- i = code.indexOf('\n', i);
170
- if (i === -1) break;
171
- continue;
172
- }
173
- // Skip multi-line comments
174
- if (char === '/' && next === '*') {
175
- i = code.indexOf('*'+'/', i + 2);
176
- if (i === -1) break;
177
- i += 2;
178
- continue;
179
- }
180
- // Skip strings and template literals
181
- if (char === "'" || char === '"' || char === '`') {
182
- let quote = char;
183
- i++;
184
- while (i < code.length) {
185
- if (code[i] === '\\') i += 2;
186
- else if (code[i] === quote) { i++; break; }
187
- else i++;
188
- }
189
- continue;
190
- }
191
- // Skip regex literals (simple heuristic)
192
- if (char === '/') {
193
- let prev = code.slice(Math.max(0, i - 10), i).trim();
194
- // If / is preceded by something that indicates an operator or start of expression
195
- if (/[=(,;:[!&|?]$|return$|yield$|case$/.test(prev)) {
196
- i++;
197
- while (i < code.length) {
198
- if (code[i] === '\\') i += 2;
199
- else if (code[i] === '[') { // Skip character classes
200
- i++;
201
- while (i < code.length && code[i] !== ']') {
202
- if (code[i] === '\\') i += 2;
203
- else i++;
204
- }
205
- i++;
206
- }
207
- else if (code[i] === '/') { i++; break; }
208
- else i++;
209
- }
210
- continue;
211
- }
212
- }
213
- // Check for h(this)`
214
- if (char === 'h' && code.slice(i, i + 8) === 'h(this)`') {
215
- i += 8;
216
- // We are now inside the template literal.
217
- // Skip whitespace and HTML comments
218
- while (i < code.length) {
219
- // Skip JS template literal end (shouldn't happen before tag, but for safety)
220
- if (code[i] === '`') return null;
221
-
222
- // Skip whitespace
223
- if (/\s/.test(code[i])) { i++; continue; }
224
-
225
- // Skip HTML comments <!-- ... -->
226
- if (code.slice(i, i + 4) === '<!--') {
227
- i = code.indexOf('-->', i + 4);
228
- if (i === -1) return null;
229
- i += 3;
230
- continue;
231
- }
232
-
233
- // Find the first tag
234
- if (code[i] === '<') {
235
- let start = ++i;
236
- while (i < code.length && /[a-zA-Z0-9-]/.test(code[i])) i++;
237
- return code.slice(start, i);
238
- }
239
-
240
- // If we encounter anything else (like text before a tag),
241
- // we can keep looking or return null depending on how strict we want to be.
242
- // For now, let's just skip non-tag characters.
243
- i++;
244
- }
245
- }
246
- i++;
247
- }
248
- return null;
249
- }
250
- */
251
- }
252
-
253
-
254
- /**
255
- * Assign fields from `src` to `dest` if they exist in `dest` and their names are not in the `ignore` list.
256
- * When a value in `src` is a string and the existing value in `dest` is a boolean, number, or Date,
257
- * it will be converted to that type.
258
- * This is often used in class constructors that accept an object of arguments.
259
- * @param {object} dest
260
- * @param {?object} src
261
- * @param {string[]} [ignore=[]] */
262
- export function assignFields(dest, src, ignore=[]) {
263
- for (let name in src || {}) {
264
- if (name in dest && !ignore.includes(name)) {
265
- const descriptor = Object.getOwnPropertyDescriptor(dest, name)
266
- || Object.getOwnPropertyDescriptor(Object.getPrototypeOf(dest), name); // Also find (parent?) setters. Is this necssary?
267
- if (!descriptor || descriptor.writable || descriptor.set) {
268
- let srcVal = src[name];
269
- let destVal = dest[name];
270
- if (typeof src[name] === 'string') {
271
- if (typeof destVal === 'boolean') // empty string (when an attribute is present with no value) is true
272
- dest[name] = ![false, 'false', 0, '0'].includes(srcVal);
273
- else if (typeof destVal === 'number')
274
- dest[name] = Number(srcVal);
275
- else if (destVal instanceof Date) {
276
- dest[name] = new Date(srcVal); // TODO: read it as UTC 0 by default.
277
- }
278
- else
279
- dest[name] = srcVal;
280
- }
281
- else
282
- dest[name] = srcVal;
283
- }
284
- }
285
- }
286
- }
1
+ /*
2
+ ┏┓ ┓ •
3
+ ┗┓┏┓┃┏┓┏┓┓╋▗▖
4
+ ┗┛┗┛┗┗┻╹ ╹╹┗
5
+ JavasCript UI library
6
+ @license MIT
7
+ @copyright Vorticode LLC
8
+ https://vorticode.github.io/solarite/ */
9
+ import h from './h.js';
10
+ export default h;
11
+ export {default as delve} from './delve.js';
12
+ export {default as Template} from './Template.js';
13
+ export {default as toEl} from './toEl.js';
14
+ export {assignAttributes} from './assignAttributes.js';
15
+ export {svg} from './h.js';
16
+ export {getEventBinding} from './PathToAttribValue.js';
17
+ export {Fragment} from './jsx.js';
18
+
19
+ // Experimental:
20
+ //--------------
21
+ export {default as Globals} from './Globals.js';
22
+ export {default as SolariteUtil} from './Util.js';
23
+
24
+ // Deprecated:
25
+ //--------------
26
+ export {default as h} from './h.js'; // Named exports for h() are deprecated.
27
+
28
+ // HtmlParser, NodeGroup, and Shell are internal; tests import them directly from their modules.
29
+
30
+
31
+
32
+ // Solarite Class:
33
+ //--------------
34
+ import Util from "./Util.js";
35
+ import Globals from "./Globals.js";
36
+
37
+ /**
38
+ * Intercept the construct call to auto-define the class before the constructor is called. */
39
+ let HTMLElementAutoDefine = new Proxy(HTMLElement, {
40
+ construct(Parent, args, Class) {
41
+
42
+ // 1. Call customElements.define() automatically.
43
+ Util.defineClass(Class);
44
+
45
+ // 2. This line is equivalent the to super() call to HTMLElement:
46
+ return Reflect.construct(Parent, args, Class);
47
+ }
48
+ });
49
+
50
+ /**
51
+ * Solarite provides more features if your web component extends Solarite instead of HTMLElement.
52
+ *
53
+ * Reasons to inherit from Solarite instead of HTMLElement.
54
+ * 1. customElements.define() is called automatically when you create the first instance.
55
+ * 2. Calls render() when added to the DOM, if it hasn't been called already.
56
+ * 3. Populates the attribs argument to the constructor when instantiated from regular html outside a template string.
57
+ * It parses JSON from DOM attribute values surrouned with '${...}'
58
+ * 4. Shows an error if render() isn't defined.
59
+ *
60
+ * Advantages to inheriting from HTMLElement
61
+ * 1. We can inherit from things like HTMLTableRowElement directly.
62
+ * 2. There's less magic, since everyone is familiar with defining custom elements.
63
+ * 3. No confusion about how the class name becomes a tag name.
64
+ * @extends {HTMLElement} */
65
+ export class Solarite extends HTMLElementAutoDefine {
66
+
67
+ /**
68
+ * @param attribs {?Record<string, any>} */
69
+ constructor(attribs=null) {
70
+ super();
71
+
72
+ if (attribs) {
73
+ if (typeof attribs !== 'object')
74
+ throw new Error('First argument to custom element constructor must be an object.');
75
+
76
+ // 1. Populate attribs if it's an empty object.
77
+ if (attribs && !Object.keys(attribs).length) {
78
+ let attribs2 = Solarite.getAttribs(this);
79
+ for (let name in attribs2) {
80
+ attribs[name] = attribs2[name];
81
+ }
82
+ }
83
+
84
+ // 2. Populate fields from attribs.
85
+ // This does nothing because the fields are overwritten by the child class after this super() constructor executes.
86
+ //for (let name in attribs || {}) {
87
+ // if (name in this) {
88
+ // const descriptor = Object.getOwnPropertyDescriptor(this, name);
89
+ // if (!descriptor || descriptor.writable || descriptor.set)
90
+ // this[name] = attribs[name];
91
+ // }
92
+ //}
93
+ }
94
+
95
+ // 3. Wrap render function so it always provides the attribs argument.
96
+ // Disabled because this gives us strings for attribute values when we call render manually.
97
+ // Instead of values given from ${...} expressions.
98
+ // let originalRender = this.render;
99
+ // this.render = (attribs, changed=true) => {
100
+ // if (!attribs) // If we have to look up the attribs, we don't know if they changed or not.
101
+ // attribs = Solarite.getAttribs(this);
102
+ // originalRender.call(this, attribs, changed);
103
+ // }
104
+ }
105
+
106
+ 'render'() {
107
+ throw new Error('render() is not defined for ' + this.constructor.name);
108
+ }
109
+
110
+ /**
111
+ * Call render() only if it hasn't already been called. */
112
+ 'renderFirstTime'() {
113
+ if (!Globals.rendered.has(this)) {
114
+ let attribs = Solarite.getAttribs(this);
115
+ this.render(attribs); // calls Globals.rendered.add(this); inside the call to h()'...'.
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Called automatically by the browser. */
121
+ 'connectedCallback'() { // quoted so terser doesn't remove it.
122
+ this.renderFirstTime();
123
+ }
124
+
125
+ static 'define'(tagName=null) {
126
+ Util.defineClass(this, tagName);
127
+ }
128
+
129
+ static 'getAttribs'(el) {
130
+ let result = Util.attribsToObject(el);
131
+ for (let name in result) {
132
+ let val = result[name];
133
+
134
+ // We don't do eval because that seems too dangerous.
135
+ if (val.startsWith('${') && val.endsWith('}'))
136
+ result[name] = JSON.parse(val.slice(2, -1));
137
+ }
138
+ return result;
139
+ }
140
+
141
+
142
+ // TODO: Do we want to use this to get the tag name from the render() function, instead of having the user define it?
143
+ /**
144
+ * Get the tag name for a class, as defined by the tag used in render().
145
+ *
146
+ * This will parse the JavaScript code of the render() function to find the tag name.
147
+ * It will itarage every character, keeping track of quotes and comments so it can
148
+ * skip them until it finds the tag name passed to h(this)`<tagname>` inside the render() function.
149
+ *
150
+ * */
151
+ /*
152
+ static getTagName(Class) {
153
+ let code = Class.prototype.render.toString();
154
+ let i = 0;
155
+ while (i < code.length) {
156
+ let char = code[i];
157
+ let next = code[i + 1];
158
+
159
+ // Skip single line comments
160
+ if (char === '/' && next === '/') {
161
+ i = code.indexOf('\n', i);
162
+ if (i === -1) break;
163
+ continue;
164
+ }
165
+ // Skip multi-line comments
166
+ if (char === '/' && next === '*') {
167
+ i = code.indexOf('*'+'/', i + 2);
168
+ if (i === -1) break;
169
+ i += 2;
170
+ continue;
171
+ }
172
+ // Skip strings and template literals
173
+ if (char === "'" || char === '"' || char === '`') {
174
+ let quote = char;
175
+ i++;
176
+ while (i < code.length) {
177
+ if (code[i] === '\\') i += 2;
178
+ else if (code[i] === quote) { i++; break; }
179
+ else i++;
180
+ }
181
+ continue;
182
+ }
183
+ // Skip regex literals (simple heuristic)
184
+ if (char === '/') {
185
+ let prev = code.slice(Math.max(0, i - 10), i).trim();
186
+ // If / is preceded by something that indicates an operator or start of expression
187
+ if (/[=(,;:[!&|?]$|return$|yield$|case$/.test(prev)) {
188
+ i++;
189
+ while (i < code.length) {
190
+ if (code[i] === '\\') i += 2;
191
+ else if (code[i] === '[') { // Skip character classes
192
+ i++;
193
+ while (i < code.length && code[i] !== ']') {
194
+ if (code[i] === '\\') i += 2;
195
+ else i++;
196
+ }
197
+ i++;
198
+ }
199
+ else if (code[i] === '/') { i++; break; }
200
+ else i++;
201
+ }
202
+ continue;
203
+ }
204
+ }
205
+ // Check for h(this)`
206
+ if (char === 'h' && code.slice(i, i + 8) === 'h(this)`') {
207
+ i += 8;
208
+ // We are now inside the template literal.
209
+ // Skip whitespace and HTML comments
210
+ while (i < code.length) {
211
+ // Skip JS template literal end (shouldn't happen before tag, but for safety)
212
+ if (code[i] === '`') return null;
213
+
214
+ // Skip whitespace
215
+ if (/\s/.test(code[i])) { i++; continue; }
216
+
217
+ // Skip HTML comments <!-- ... -->
218
+ if (code.slice(i, i + 4) === '<!--') {
219
+ i = code.indexOf('-->', i + 4);
220
+ if (i === -1) return null;
221
+ i += 3;
222
+ continue;
223
+ }
224
+
225
+ // Find the first tag
226
+ if (code[i] === '<') {
227
+ let start = ++i;
228
+ while (i < code.length && /[a-zA-Z0-9-]/.test(code[i])) i++;
229
+ return code.slice(start, i);
230
+ }
231
+
232
+ // If we encounter anything else (like text before a tag),
233
+ // we can keep looking or return null depending on how strict we want to be.
234
+ // For now, let's just skip non-tag characters.
235
+ i++;
236
+ }
237
+ }
238
+ i++;
239
+ }
240
+ return null;
241
+ }
242
+ */
243
+ }