gonia 0.2.0 → 0.2.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.
@@ -3,5 +3,5 @@
3
3
  *
4
4
  * @packageDocumentation
5
5
  */
6
- export { render, registerDirective } from './render.js';
6
+ export { render, registerDirective, registerService } from './render.js';
7
7
  export type { DirectiveRegistry } from './render.js';
@@ -3,4 +3,4 @@
3
3
  *
4
4
  * @packageDocumentation
5
5
  */
6
- export { render, registerDirective } from './render.js';
6
+ export { render, registerDirective, registerService } from './render.js';
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @packageDocumentation
5
5
  */
6
- import { parseHTML } from 'linkedom/worker';
6
+ import { Window } from 'happy-dom';
7
7
  import { Mode, DirectivePriority, getDirective } from '../types.js';
8
8
  import { createContext } from '../context.js';
9
9
  import { processNativeSlot } from '../directives/slot.js';
@@ -12,6 +12,20 @@ import { FOR_PROCESSED_ATTR, FOR_TEMPLATE_ATTR } from '../directives/for.js';
12
12
  import { IF_PROCESSED_ATTR } from '../directives/if.js';
13
13
  import { resolveDependencies as resolveInjectables } from '../inject.js';
14
14
  import { resolveContext } from '../context-registry.js';
15
+ /**
16
+ * Decode HTML entities that happy-dom doesn't decode.
17
+ *
18
+ * @internal
19
+ */
20
+ function decodeHTMLEntities(str) {
21
+ return str
22
+ .replace(/&#(\d+);/g, (_, code) => String.fromCharCode(Number(code)))
23
+ .replace(/"/g, '"')
24
+ .replace(/'/g, "'")
25
+ .replace(/&lt;/g, '<')
26
+ .replace(/&gt;/g, '>')
27
+ .replace(/&amp;/g, '&');
28
+ }
15
29
  /** Registered services */
16
30
  let services = new Map();
17
31
  const selectorCache = new WeakMap();
@@ -123,10 +137,11 @@ function createServerResolverConfig(el, rootState) {
123
137
  * ```
124
138
  */
125
139
  export async function render(html, state, registry) {
126
- const { document, MutationObserver } = parseHTML('<!DOCTYPE html><html><body></body></html>');
140
+ const window = new Window();
141
+ const document = window.document;
127
142
  const index = [];
128
143
  const selector = getSelector(registry);
129
- const observer = new MutationObserver((mutations) => {
144
+ const observer = new window.MutationObserver((mutations) => {
130
145
  for (const mutation of mutations) {
131
146
  for (const node of mutation.addedNodes) {
132
147
  if (node.nodeType !== 1)
@@ -151,6 +166,27 @@ export async function render(html, state, registry) {
151
166
  });
152
167
  continue;
153
168
  }
169
+ // Handle g-scope elements that don't have other directives
170
+ // Add a placeholder entry so they get processed
171
+ if (match.hasAttribute('g-scope')) {
172
+ let hasDirective = false;
173
+ for (const [name] of registry) {
174
+ if (match.hasAttribute(`g-${name}`)) {
175
+ hasDirective = true;
176
+ break;
177
+ }
178
+ }
179
+ if (!hasDirective) {
180
+ index.push({
181
+ el: match,
182
+ name: 'scope',
183
+ directive: null,
184
+ expr: '',
185
+ priority: DirectivePriority.STRUCTURAL,
186
+ isNativeSlot: false
187
+ });
188
+ }
189
+ }
154
190
  for (const [name, directive] of registry) {
155
191
  const attr = match.getAttribute(`g-${name}`);
156
192
  if (attr !== null) {
@@ -160,7 +196,7 @@ export async function render(html, state, registry) {
160
196
  el: match,
161
197
  name,
162
198
  directive,
163
- expr: attr,
199
+ expr: decodeHTMLEntities(attr),
164
200
  priority: directive.priority ?? DirectivePriority.NORMAL,
165
201
  using: registration?.options.using
166
202
  });
@@ -226,7 +262,7 @@ export async function render(html, state, registry) {
226
262
  // Process g-scope first (inline scope initialization)
227
263
  const scopeAttr = el.getAttribute('g-scope');
228
264
  if (scopeAttr) {
229
- const scopeValues = ctx.eval(scopeAttr);
265
+ const scopeValues = ctx.eval(decodeHTMLEntities(scopeAttr));
230
266
  if (scopeValues && typeof scopeValues === 'object') {
231
267
  Object.assign(state, scopeValues);
232
268
  }
@@ -235,7 +271,7 @@ export async function render(html, state, registry) {
235
271
  for (const attr of [...el.attributes]) {
236
272
  if (attr.name.startsWith('g-bind:')) {
237
273
  const targetAttr = attr.name.slice('g-bind:'.length);
238
- const value = ctx.eval(attr.value);
274
+ const value = ctx.eval(decodeHTMLEntities(attr.value));
239
275
  if (value === null || value === undefined) {
240
276
  el.removeAttribute(targetAttr);
241
277
  }
@@ -252,6 +288,10 @@ export async function render(html, state, registry) {
252
288
  if (item.isNativeSlot) {
253
289
  processNativeSlot(item.el);
254
290
  }
291
+ else if (item.directive === null) {
292
+ // Placeholder for g-scope - already processed above
293
+ continue;
294
+ }
255
295
  else {
256
296
  const config = createServerResolverConfig(item.el, state);
257
297
  const args = resolveInjectables(item.directive, item.expr, item.el, ctx.eval.bind(ctx), config, item.using);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gonia",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "A lightweight, SSR-first reactive UI library with declarative directives",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -46,7 +46,7 @@
46
46
  }
47
47
  },
48
48
  "dependencies": {
49
- "linkedom": "^0.18.0",
49
+ "happy-dom": "^17.4.4",
50
50
  "tinyglobby": "^0.2.15"
51
51
  },
52
52
  "peerDependencies": {