@dinoreic/fez 0.4.0 → 0.5.2
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.
- package/README.md +723 -198
- package/bin/fez +16 -6
- package/bin/fez-compile +347 -0
- package/bin/fez-debug +25 -0
- package/bin/fez-index +16 -4
- package/bin/refactor +699 -0
- package/dist/fez.js +142 -33
- package/dist/fez.js.map +4 -4
- package/fez.d.ts +533 -0
- package/package.json +25 -15
- package/src/fez/compile.js +396 -164
- package/src/fez/connect.js +250 -143
- package/src/fez/defaults.js +275 -84
- package/src/fez/instance.js +673 -514
- package/src/fez/lib/await-helper.js +64 -0
- package/src/fez/lib/global-state.js +22 -4
- package/src/fez/lib/index.js +140 -0
- package/src/fez/lib/localstorage.js +44 -0
- package/src/fez/lib/n.js +38 -23
- package/src/fez/lib/pubsub.js +208 -0
- package/src/fez/lib/svelte-template-lib.js +339 -0
- package/src/fez/lib/svelte-template.js +472 -0
- package/src/fez/lib/template.js +114 -119
- package/src/fez/morph.js +384 -0
- package/src/fez/root.js +284 -164
- package/src/fez/utility.js +319 -149
- package/src/fez/utils/dump.js +114 -84
- package/src/fez/utils/highlight_all.js +1 -1
- package/src/fez.js +65 -43
- package/src/rollup.js +1 -1
- package/src/svelte-cde-adapter.coffee +21 -12
- package/src/fez/vendor/idiomorph.js +0 -860
package/src/fez/root.js
CHANGED
|
@@ -1,206 +1,326 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
//
|
|
1
|
+
/**
|
|
2
|
+
* Fez - Main Framework Object
|
|
3
|
+
*
|
|
4
|
+
* This file contains:
|
|
5
|
+
* - Main Fez function (component registration and lookup)
|
|
6
|
+
* - Component registry
|
|
7
|
+
* - CSS utilities
|
|
8
|
+
* - Pub/Sub system
|
|
9
|
+
* - DOM morphing
|
|
10
|
+
* - Error handling
|
|
11
|
+
* - Temporary store
|
|
12
|
+
*
|
|
13
|
+
* For component instance methods, see instance.js
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// =============================================================================
|
|
17
|
+
// IMPORTS
|
|
18
|
+
// =============================================================================
|
|
19
|
+
|
|
20
|
+
import Gobber from "./vendor/gobber.js";
|
|
21
|
+
import { fezMorph } from "./morph.js";
|
|
22
|
+
import objectDump from "./utils/dump.js";
|
|
23
|
+
import highlightAll from "./utils/highlight_all.js";
|
|
24
|
+
import connect from "./connect.js";
|
|
25
|
+
import compile from "./compile.js";
|
|
26
|
+
import state from "./lib/global-state.js";
|
|
27
|
+
import createTemplate from "./lib/template.js";
|
|
28
|
+
import { subscribe, publish } from "./lib/pubsub.js";
|
|
29
|
+
import fezLocalStorage from "./lib/localstorage.js";
|
|
30
|
+
import fezAwait from "./lib/await-helper.js";
|
|
31
|
+
import index from "./lib/index.js";
|
|
32
|
+
|
|
33
|
+
// =============================================================================
|
|
34
|
+
// MAIN FEZ FUNCTION
|
|
35
|
+
// =============================================================================
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Main Fez function - register or find components
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* Fez('ui-foo', class { ... }) // Register component
|
|
42
|
+
* Fez('ui-foo') // Find first instance
|
|
43
|
+
* Fez(123) // Find by UID
|
|
44
|
+
* Fez(domNode) // Find from DOM node
|
|
45
|
+
* Fez('ui-foo', fn) // Find all & execute callback
|
|
46
|
+
*
|
|
47
|
+
* @param {string|number|Node} name - Component name, UID, or DOM node
|
|
48
|
+
* @param {Class|Function} [klass] - Component class or callback
|
|
49
|
+
* @returns {FezBase|Array|void}
|
|
50
|
+
*/
|
|
17
51
|
const Fez = (name, klass) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
}
|
|
52
|
+
// Find by UID
|
|
53
|
+
if (typeof name === "number") {
|
|
54
|
+
const fez = Fez.instances.get(name);
|
|
55
|
+
if (fez) return fez;
|
|
56
|
+
Fez.onError("lookup", `Instance with UID "${name}" not found. Component may have been destroyed or never created.`, { uid: name });
|
|
57
|
+
return;
|
|
25
58
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (isPureFn) {
|
|
31
|
-
const list = Array
|
|
32
|
-
.from(document.querySelectorAll(`.fez.fez-${name}`))
|
|
33
|
-
.filter( n => n.fez )
|
|
34
|
-
|
|
35
|
-
list.forEach( el => klass(el.fez) )
|
|
36
|
-
return list
|
|
37
|
-
} else if (typeof klass != 'function') {
|
|
38
|
-
return Fez.find(name, klass)
|
|
39
|
-
} else {
|
|
40
|
-
return connect(name, klass)
|
|
41
|
-
}
|
|
42
|
-
} else {
|
|
43
|
-
const node = name.nodeName ? name.closest('.fez') : (
|
|
44
|
-
document.querySelector( name.includes('#') ? name : `.fez.fez-${name}` )
|
|
45
|
-
)
|
|
46
|
-
if (node) {
|
|
47
|
-
if (node.fez) {
|
|
48
|
-
return node.fez
|
|
49
|
-
} else {
|
|
50
|
-
Fez.error(`node "${name}" has no Fez attached.`)
|
|
51
|
-
}
|
|
52
|
-
} else {
|
|
53
|
-
Fez.error(`node "${name}" not found.`)
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
} else {
|
|
57
|
-
Fez.error('Fez() ?')
|
|
59
|
+
|
|
60
|
+
if (!name) {
|
|
61
|
+
Fez.onError("lookup", "Fez() called without arguments. Expected component name, UID, or DOM node.");
|
|
62
|
+
return;
|
|
58
63
|
}
|
|
59
|
-
}
|
|
60
64
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
// With second argument
|
|
66
|
+
if (klass) {
|
|
67
|
+
const isPureFn =
|
|
68
|
+
typeof klass === "function" &&
|
|
69
|
+
!/^\s*class/.test(klass.toString()) &&
|
|
70
|
+
!/\b(this|new)\b/.test(klass.toString());
|
|
71
|
+
|
|
72
|
+
// Fez('name', callback) - find all & execute
|
|
73
|
+
if (isPureFn) {
|
|
74
|
+
const list = Array.from(
|
|
75
|
+
document.querySelectorAll(`.fez.fez-${name}`),
|
|
76
|
+
).filter((n) => n.fez);
|
|
77
|
+
list.forEach((el) => klass(el.fez));
|
|
78
|
+
return list;
|
|
79
|
+
}
|
|
64
80
|
|
|
65
|
-
Fez
|
|
66
|
-
|
|
81
|
+
// Fez('name', selector) - find with context
|
|
82
|
+
if (typeof klass !== "function") {
|
|
83
|
+
return Fez.find(name, klass);
|
|
84
|
+
}
|
|
67
85
|
|
|
68
|
-
|
|
69
|
-
|
|
86
|
+
// Fez('name', class) - register component
|
|
87
|
+
return connect(name, klass);
|
|
70
88
|
}
|
|
71
89
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
90
|
+
// Find instance by name or node
|
|
91
|
+
const node = name.nodeName
|
|
92
|
+
? name.closest(".fez")
|
|
93
|
+
: document.querySelector(name.includes("#") ? name : `.fez.fez-${name}`);
|
|
75
94
|
|
|
76
|
-
|
|
95
|
+
if (!node) {
|
|
96
|
+
Fez.onError("lookup", `Component "${name}" not found in DOM. Ensure the component is defined and rendered.`, { componentName: name });
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
77
99
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return
|
|
81
|
-
} else {
|
|
82
|
-
console.error('Fez node connector not found', onode, node)
|
|
100
|
+
if (!node.fez) {
|
|
101
|
+
Fez.onError("lookup", `DOM node "${name}" exists but has no Fez instance attached. Component may not be initialized yet.`, { node, tagName: name });
|
|
102
|
+
return;
|
|
83
103
|
}
|
|
84
|
-
}
|
|
85
104
|
|
|
105
|
+
return node.fez;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// =============================================================================
|
|
109
|
+
// COMPONENT REGISTRY
|
|
110
|
+
// =============================================================================
|
|
111
|
+
|
|
112
|
+
/** Unified component index - Fez.index['name'] = { class, meta, demo, info, source } */
|
|
113
|
+
Fez.index = index;
|
|
114
|
+
|
|
115
|
+
/** Counter for unique instance IDs */
|
|
116
|
+
Fez.instanceCount = 0;
|
|
117
|
+
|
|
118
|
+
/** Active component instances by UID */
|
|
119
|
+
Fez.instances = new Map();
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Find a component instance from a DOM node
|
|
123
|
+
* @param {Node|string} onode - DOM node or selector
|
|
124
|
+
* @param {string} [name] - Optional component name filter
|
|
125
|
+
* @returns {FezBase|undefined}
|
|
126
|
+
*/
|
|
127
|
+
Fez.find = (onode, name) => {
|
|
128
|
+
let node =
|
|
129
|
+
typeof onode === "string" ? document.body.querySelector(onode) : onode;
|
|
130
|
+
|
|
131
|
+
// jQuery compatibility
|
|
132
|
+
if (typeof node.val === "function") node = node[0];
|
|
133
|
+
|
|
134
|
+
const selector = name ? `.fez.fez-${name}` : ".fez";
|
|
135
|
+
const closestNode = node.closest(selector);
|
|
136
|
+
|
|
137
|
+
if (closestNode?.fez) return closestNode.fez;
|
|
138
|
+
|
|
139
|
+
Fez.onError("find", `Node connector not found. Selector: "${selector}", node: ${onode}`, {
|
|
140
|
+
original: onode,
|
|
141
|
+
resolved: node,
|
|
142
|
+
selector,
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// =============================================================================
|
|
147
|
+
// CSS UTILITIES
|
|
148
|
+
// =============================================================================
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Generate unique CSS class from CSS text (via Goober)
|
|
152
|
+
* @param {string} text - CSS rules
|
|
153
|
+
* @returns {string} Generated class name
|
|
154
|
+
*/
|
|
86
155
|
Fez.cssClass = (text) => {
|
|
87
|
-
|
|
88
|
-
|
|
156
|
+
// In test environments without proper DOM, goober may fail
|
|
157
|
+
// Return a placeholder class name based on hash
|
|
158
|
+
try {
|
|
159
|
+
return Gobber.css(text);
|
|
160
|
+
} catch {
|
|
161
|
+
// Fallback: generate simple hash-based class name
|
|
162
|
+
let hash = 0;
|
|
163
|
+
for (let i = 0; i < text.length; i++) {
|
|
164
|
+
hash = ((hash << 5) - hash + text.charCodeAt(i)) | 0;
|
|
165
|
+
}
|
|
166
|
+
return "fez-" + Math.abs(hash).toString(36);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
89
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Register global CSS styles
|
|
172
|
+
* @param {string|Function} cssClass - CSS text or function
|
|
173
|
+
* @param {Object} opts - { name, wrap }
|
|
174
|
+
* @returns {string} Generated class name
|
|
175
|
+
*/
|
|
90
176
|
Fez.globalCss = (cssClass, opts = {}) => {
|
|
91
|
-
if (typeof cssClass ===
|
|
92
|
-
cssClass = cssClass()
|
|
93
|
-
}
|
|
177
|
+
if (typeof cssClass === "function") cssClass = cssClass();
|
|
94
178
|
|
|
95
|
-
if (cssClass.includes(
|
|
179
|
+
if (cssClass.includes(":")) {
|
|
96
180
|
let text = cssClass
|
|
97
181
|
.split("\n")
|
|
98
|
-
.filter(line =>
|
|
99
|
-
.join("\n")
|
|
182
|
+
.filter((line) => !/^\s*\/\//.test(line))
|
|
183
|
+
.join("\n");
|
|
100
184
|
|
|
101
|
-
if (opts.wrap) {
|
|
102
|
-
|
|
103
|
-
|
|
185
|
+
if (opts.wrap) text = `:fez { ${text} }`;
|
|
186
|
+
text = text.replace(/\:fez|\:host/, `.fez.fez-${opts.name}`);
|
|
187
|
+
cssClass = Fez.cssClass(text);
|
|
188
|
+
}
|
|
104
189
|
|
|
105
|
-
|
|
190
|
+
Fez.onReady(() => document.body.parentElement.classList.add(cssClass));
|
|
191
|
+
return cssClass;
|
|
192
|
+
};
|
|
106
193
|
|
|
107
|
-
|
|
108
|
-
|
|
194
|
+
// =============================================================================
|
|
195
|
+
// DOM MORPHING
|
|
196
|
+
// =============================================================================
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Morph DOM node to new state (via fez-morph)
|
|
200
|
+
* Child fez components are automatically preserved (skipped from morphing)
|
|
201
|
+
* Use fez-keep attribute for explicit element preservation
|
|
202
|
+
* @param {Element} target - Element to morph
|
|
203
|
+
* @param {Element} newNode - New state
|
|
204
|
+
*/
|
|
205
|
+
Fez.morphdom = (target, newNode) => {
|
|
206
|
+
fezMorph(target, newNode, {
|
|
207
|
+
// Preserve child fez components - skip morphing them entirely
|
|
208
|
+
skipNode: (oldNode) => {
|
|
209
|
+
if (
|
|
210
|
+
oldNode.classList?.contains("fez") &&
|
|
211
|
+
oldNode.fez &&
|
|
212
|
+
!oldNode.fez._destroyed
|
|
213
|
+
) {
|
|
214
|
+
if (Fez.LOG) {
|
|
215
|
+
console.log(
|
|
216
|
+
`Fez: preserved child component ${oldNode.fez.fezName} (UID ${oldNode.fez.UID})`,
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
return false;
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
// Cleanup destroyed fez components
|
|
225
|
+
beforeRemove: (node) => {
|
|
226
|
+
if (node.classList?.contains("fez") && node.fez) {
|
|
227
|
+
node.fez.fezOnDestroy();
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
};
|
|
109
232
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
233
|
+
// =============================================================================
|
|
234
|
+
// PUB/SUB SYSTEM (see lib/pubsub.js)
|
|
235
|
+
// =============================================================================
|
|
113
236
|
|
|
114
|
-
|
|
115
|
-
|
|
237
|
+
Fez.subscribe = subscribe;
|
|
238
|
+
Fez.publish = publish;
|
|
116
239
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
240
|
+
// =============================================================================
|
|
241
|
+
// LOCAL STORAGE (see lib/localstorage.js)
|
|
242
|
+
// =============================================================================
|
|
120
243
|
|
|
121
|
-
Fez.
|
|
122
|
-
Array.from(target.attributes).forEach(attr => {
|
|
123
|
-
newNode.setAttribute(attr.name, attr.value)
|
|
124
|
-
})
|
|
244
|
+
Fez.localStorage = fezLocalStorage;
|
|
125
245
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
246
|
+
// =============================================================================
|
|
247
|
+
// ASYNC AWAIT HELPER (see lib/await-helper.js)
|
|
248
|
+
// =============================================================================
|
|
129
249
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
Fez._subs[channel] ||= []
|
|
140
|
-
Fez._subs[channel].forEach((el) => {
|
|
141
|
-
el[1].bind(el[0])(...args)
|
|
142
|
-
})
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
Fez.error = (text, show) => {
|
|
146
|
-
text = `Fez: ${text}`
|
|
147
|
-
console.error(text)
|
|
250
|
+
Fez.fezAwait = fezAwait;
|
|
251
|
+
|
|
252
|
+
// =============================================================================
|
|
253
|
+
// ERROR HANDLING & LOGGING
|
|
254
|
+
// =============================================================================
|
|
255
|
+
|
|
256
|
+
Fez.consoleError = (text, show) => {
|
|
257
|
+
text = `Fez: ${text}`;
|
|
258
|
+
console.error(text);
|
|
148
259
|
if (show) {
|
|
149
|
-
return `<span style="border: 1px solid red; font-size: 14px; padding: 3px 7px; background: #fee; border-radius: 4px;">${text}</span
|
|
260
|
+
return `<span style="border: 1px solid red; font-size: 14px; padding: 3px 7px; background: #fee; border-radius: 4px;">${text}</span>`;
|
|
150
261
|
}
|
|
151
|
-
}
|
|
262
|
+
};
|
|
152
263
|
|
|
153
|
-
Fez.
|
|
154
|
-
if (Fez.LOG
|
|
155
|
-
|
|
156
|
-
console.log(`Fez: ${text}`)
|
|
264
|
+
Fez.consoleLog = (text) => {
|
|
265
|
+
if (Fez.LOG) {
|
|
266
|
+
console.log(`Fez: ${String(text).substring(0, 180)}`);
|
|
157
267
|
}
|
|
158
|
-
}
|
|
268
|
+
};
|
|
159
269
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
270
|
+
/**
|
|
271
|
+
* Enhanced error handler with component context
|
|
272
|
+
* @param {string} kind - Error category (e.g., 'template', 'lifecycle', 'morph')
|
|
273
|
+
* @param {string|Error} message - Error message or Error object
|
|
274
|
+
* @param {Object} [context] - Additional context (component name, props, etc.)
|
|
275
|
+
* @returns {string} Formatted error message
|
|
276
|
+
*/
|
|
277
|
+
Fez.onError = (kind, message, context) => {
|
|
278
|
+
// Extract component name from context or message
|
|
279
|
+
let componentName = context?.componentName || context?.name;
|
|
280
|
+
|
|
281
|
+
// Try to extract component name from message if not in context
|
|
282
|
+
if (!componentName && typeof message === "string") {
|
|
283
|
+
const match = message.match(/<([^>]+)>/);
|
|
284
|
+
if (match) componentName = match[1];
|
|
164
285
|
}
|
|
165
286
|
|
|
166
|
-
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
store: new Map(),
|
|
172
|
-
counter: 0,
|
|
287
|
+
// Format the error message with component context
|
|
288
|
+
const prefix = componentName ? ` [${componentName}]` : "";
|
|
289
|
+
const errorMsg =
|
|
290
|
+
typeof message === "string" ? message : message?.message || String(message);
|
|
291
|
+
const fullMessage = `Fez ${kind}:${prefix} ${errorMsg}`;
|
|
173
292
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
get(key) {
|
|
181
|
-
return this.store.get(key);
|
|
182
|
-
},
|
|
293
|
+
// Log with context if available
|
|
294
|
+
if (context && Fez.LOG) {
|
|
295
|
+
console.error(fullMessage, context);
|
|
296
|
+
} else {
|
|
297
|
+
console.error(fullMessage);
|
|
298
|
+
}
|
|
183
299
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return value;
|
|
300
|
+
// Include stack trace for Error objects
|
|
301
|
+
if (message instanceof Error && message.stack && Fez.LOG) {
|
|
302
|
+
console.error(message.stack);
|
|
188
303
|
}
|
|
304
|
+
|
|
305
|
+
return fullMessage;
|
|
189
306
|
};
|
|
190
307
|
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
308
|
+
// =============================================================================
|
|
309
|
+
// LOAD UTILITIES & EXPORTS
|
|
310
|
+
// =============================================================================
|
|
311
|
+
|
|
312
|
+
import addUtilities from "./utility.js";
|
|
313
|
+
import cssMixin from "./utils/css_mixin.js";
|
|
314
|
+
|
|
315
|
+
addUtilities(Fez);
|
|
316
|
+
cssMixin(Fez);
|
|
196
317
|
|
|
197
|
-
Fez.compile = compile
|
|
198
|
-
Fez.
|
|
199
|
-
Fez.
|
|
200
|
-
Fez.
|
|
318
|
+
Fez.compile = compile;
|
|
319
|
+
Fez.createTemplate = createTemplate;
|
|
320
|
+
Fez.state = state;
|
|
321
|
+
Fez.log = objectDump;
|
|
322
|
+
Fez.highlightAll = highlightAll;
|
|
201
323
|
|
|
202
|
-
Fez.onReady(() =>
|
|
203
|
-
Fez.log('Fez.LOG === true, logging enabled.')
|
|
204
|
-
})
|
|
324
|
+
Fez.onReady(() => Fez.consoleLog("Fez.LOG === true, logging enabled."));
|
|
205
325
|
|
|
206
|
-
export default Fez
|
|
326
|
+
export default Fez;
|