gonia 0.3.1 → 0.3.3
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/dist/client/hydrate.js +12 -8
- package/dist/server/render.js +73 -9
- package/package.json +1 -1
package/dist/client/hydrate.js
CHANGED
|
@@ -228,11 +228,15 @@ function processElement(el, registry) {
|
|
|
228
228
|
// Check if any directive needs a scope
|
|
229
229
|
let scope = findParentScope(el, true) ?? {};
|
|
230
230
|
let directiveCreatedScope = false;
|
|
231
|
-
// Collect
|
|
232
|
-
const
|
|
231
|
+
// Collect unique directive names for conflict detection
|
|
232
|
+
const directiveNameSet = new Set();
|
|
233
233
|
for (const { name } of directives) {
|
|
234
234
|
const fullName = `g-${name}`;
|
|
235
|
-
|
|
235
|
+
const isNew = !directiveNameSet.has(fullName);
|
|
236
|
+
directiveNameSet.add(fullName);
|
|
237
|
+
// Only process first occurrence
|
|
238
|
+
if (!isNew)
|
|
239
|
+
continue;
|
|
236
240
|
const registration = getDirective(fullName);
|
|
237
241
|
if (!directiveCreatedScope && directiveNeedsScope(fullName)) {
|
|
238
242
|
// Create a new scope that inherits from parent
|
|
@@ -246,7 +250,7 @@ function processElement(el, registry) {
|
|
|
246
250
|
}
|
|
247
251
|
// Apply assigns with conflict detection
|
|
248
252
|
if (directiveCreatedScope) {
|
|
249
|
-
applyAssigns(scope,
|
|
253
|
+
applyAssigns(scope, [...directiveNameSet]);
|
|
250
254
|
}
|
|
251
255
|
const ctx = createContext(Mode.CLIENT, scope);
|
|
252
256
|
contextCache.set(el, ctx);
|
|
@@ -433,16 +437,16 @@ async function processDirectiveElements() {
|
|
|
433
437
|
if (options.scope) {
|
|
434
438
|
const parentScope = findParentScope(el);
|
|
435
439
|
scope = createElementScope(el, parentScope);
|
|
436
|
-
// Collect
|
|
437
|
-
const
|
|
440
|
+
// Collect unique directive names on this element for conflict detection
|
|
441
|
+
const directiveNameSet = new Set([name]);
|
|
438
442
|
for (const attr of el.attributes) {
|
|
439
443
|
const attrReg = getDirective(attr.name);
|
|
440
444
|
if (attrReg) {
|
|
441
|
-
|
|
445
|
+
directiveNameSet.add(attr.name);
|
|
442
446
|
}
|
|
443
447
|
}
|
|
444
448
|
// Apply assigns with conflict detection
|
|
445
|
-
applyAssigns(scope,
|
|
449
|
+
applyAssigns(scope, [...directiveNameSet]);
|
|
446
450
|
}
|
|
447
451
|
else {
|
|
448
452
|
scope = findParentScope(el, true) ?? {};
|
package/dist/server/render.js
CHANGED
|
@@ -66,6 +66,22 @@ function getSelector(localRegistry) {
|
|
|
66
66
|
selectors.push('slot');
|
|
67
67
|
// Match g-scope for inline scope initialization (TODO: make prefix configurable)
|
|
68
68
|
selectors.push('[g-scope]');
|
|
69
|
+
// Match common g-bind:* attributes for dynamic binding
|
|
70
|
+
// These need to be indexed so their expressions can be evaluated with proper scope
|
|
71
|
+
selectors.push('[g-bind\\:class]');
|
|
72
|
+
selectors.push('[g-bind\\:style]');
|
|
73
|
+
selectors.push('[g-bind\\:href]');
|
|
74
|
+
selectors.push('[g-bind\\:src]');
|
|
75
|
+
selectors.push('[g-bind\\:id]');
|
|
76
|
+
selectors.push('[g-bind\\:value]');
|
|
77
|
+
selectors.push('[g-bind\\:disabled]');
|
|
78
|
+
selectors.push('[g-bind\\:checked]');
|
|
79
|
+
selectors.push('[g-bind\\:placeholder]');
|
|
80
|
+
selectors.push('[g-bind\\:title]');
|
|
81
|
+
selectors.push('[g-bind\\:alt]');
|
|
82
|
+
selectors.push('[g-bind\\:name]');
|
|
83
|
+
selectors.push('[g-bind\\:type]');
|
|
84
|
+
// Note: Can't do wildcard for data-* attributes in CSS, but hasBindAttributes handles them
|
|
69
85
|
return selectors.join(',');
|
|
70
86
|
}
|
|
71
87
|
/**
|
|
@@ -190,15 +206,41 @@ export async function render(html, state, registry) {
|
|
|
190
206
|
const window = new Window();
|
|
191
207
|
const document = window.document;
|
|
192
208
|
const index = [];
|
|
209
|
+
const indexedDirectives = new Map(); // Track indexed (element, directive) pairs
|
|
193
210
|
const selector = getSelector(registry);
|
|
211
|
+
// Helper to add to index only if not already indexed for this (element, directive) pair
|
|
212
|
+
const addToIndex = (item) => {
|
|
213
|
+
const existing = indexedDirectives.get(item.el);
|
|
214
|
+
if (existing?.has(item.name)) {
|
|
215
|
+
return false; // Already indexed
|
|
216
|
+
}
|
|
217
|
+
if (!existing) {
|
|
218
|
+
indexedDirectives.set(item.el, new Set([item.name]));
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
existing.add(item.name);
|
|
222
|
+
}
|
|
223
|
+
index.push(item);
|
|
224
|
+
return true;
|
|
225
|
+
};
|
|
194
226
|
const observer = new window.MutationObserver((mutations) => {
|
|
227
|
+
// Collect all direct addedNodes first to avoid processing them as descendants
|
|
228
|
+
const directNodes = new Set();
|
|
229
|
+
for (const mutation of mutations) {
|
|
230
|
+
for (const node of mutation.addedNodes) {
|
|
231
|
+
if (node.nodeType === 1) {
|
|
232
|
+
directNodes.add(node);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
195
236
|
for (const mutation of mutations) {
|
|
196
237
|
for (const node of mutation.addedNodes) {
|
|
197
238
|
if (node.nodeType !== 1)
|
|
198
239
|
continue;
|
|
199
240
|
const el = node;
|
|
200
241
|
const matches = el.matches(selector) ? [el] : [];
|
|
201
|
-
|
|
242
|
+
// Filter out descendants that will be processed as direct addedNodes
|
|
243
|
+
const descendants = [...el.querySelectorAll(selector)].filter(desc => !directNodes.has(desc));
|
|
202
244
|
for (const match of [...matches, ...descendants]) {
|
|
203
245
|
// Skip elements inside template content (used as placeholders)
|
|
204
246
|
if (match.closest('template')) {
|
|
@@ -206,7 +248,7 @@ export async function render(html, state, registry) {
|
|
|
206
248
|
}
|
|
207
249
|
// Handle native <slot> elements
|
|
208
250
|
if (match.tagName === 'SLOT') {
|
|
209
|
-
|
|
251
|
+
addToIndex({
|
|
210
252
|
el: match,
|
|
211
253
|
name: 'slot',
|
|
212
254
|
directive: null,
|
|
@@ -227,7 +269,7 @@ export async function render(html, state, registry) {
|
|
|
227
269
|
}
|
|
228
270
|
}
|
|
229
271
|
if (!hasDirective) {
|
|
230
|
-
|
|
272
|
+
addToIndex({
|
|
231
273
|
el: match,
|
|
232
274
|
name: 'scope',
|
|
233
275
|
directive: null,
|
|
@@ -237,6 +279,27 @@ export async function render(html, state, registry) {
|
|
|
237
279
|
});
|
|
238
280
|
}
|
|
239
281
|
}
|
|
282
|
+
// Handle g-bind:* elements that don't have other directives
|
|
283
|
+
// Add a placeholder so they get processed for dynamic attribute binding
|
|
284
|
+
if (hasBindAttributes(match)) {
|
|
285
|
+
let hasDirective = false;
|
|
286
|
+
for (const name of getDirectiveNames()) {
|
|
287
|
+
if (match.hasAttribute(name)) {
|
|
288
|
+
hasDirective = true;
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
if (!hasDirective && !match.hasAttribute('g-scope')) {
|
|
293
|
+
addToIndex({
|
|
294
|
+
el: match,
|
|
295
|
+
name: 'bind',
|
|
296
|
+
directive: null,
|
|
297
|
+
expr: '',
|
|
298
|
+
priority: DirectivePriority.NORMAL,
|
|
299
|
+
isNativeSlot: false
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
240
303
|
// Check all registered directives from global registry
|
|
241
304
|
const tagName = match.tagName.toLowerCase();
|
|
242
305
|
for (const name of getDirectiveNames()) {
|
|
@@ -247,7 +310,7 @@ export async function render(html, state, registry) {
|
|
|
247
310
|
// Check if this is a custom element directive (tag name matches)
|
|
248
311
|
if (tagName === name) {
|
|
249
312
|
if (options.template || options.scope || options.provide || options.using) {
|
|
250
|
-
|
|
313
|
+
addToIndex({
|
|
251
314
|
el: match,
|
|
252
315
|
name,
|
|
253
316
|
directive: fn,
|
|
@@ -261,7 +324,7 @@ export async function render(html, state, registry) {
|
|
|
261
324
|
// Check if this is an attribute directive
|
|
262
325
|
const attr = match.getAttribute(name);
|
|
263
326
|
if (attr !== null) {
|
|
264
|
-
|
|
327
|
+
addToIndex({
|
|
265
328
|
el: match,
|
|
266
329
|
name,
|
|
267
330
|
directive: fn,
|
|
@@ -280,7 +343,7 @@ export async function render(html, state, registry) {
|
|
|
280
343
|
const fullName = `g-${name}`;
|
|
281
344
|
if (getDirective(fullName))
|
|
282
345
|
continue;
|
|
283
|
-
|
|
346
|
+
addToIndex({
|
|
284
347
|
el: match,
|
|
285
348
|
name,
|
|
286
349
|
directive,
|
|
@@ -370,13 +433,14 @@ export async function render(html, state, registry) {
|
|
|
370
433
|
}
|
|
371
434
|
}
|
|
372
435
|
}
|
|
373
|
-
// Collect
|
|
374
|
-
const
|
|
436
|
+
// Collect unique directive names for conflict detection
|
|
437
|
+
const directiveNameSet = new Set();
|
|
375
438
|
for (const item of directives) {
|
|
376
439
|
if (!item.isNativeSlot && item.directive !== null) {
|
|
377
|
-
|
|
440
|
+
directiveNameSet.add(item.name);
|
|
378
441
|
}
|
|
379
442
|
}
|
|
443
|
+
const directiveNames = [...directiveNameSet];
|
|
380
444
|
// Check if any directive needs scope - create once if so
|
|
381
445
|
let elementScope = null;
|
|
382
446
|
for (const name of directiveNames) {
|