gonia 0.2.2 → 0.2.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.
@@ -70,8 +70,8 @@ export const slot = function slot($expr, $element, $eval, $slotContent) {
70
70
  transclude();
71
71
  }
72
72
  };
73
- slot.$inject = ['$expr', '$element', '$eval', SlotContentContext];
74
- directive('g-slot', slot);
73
+ slot.$inject = ['$expr', '$element', '$eval'];
74
+ directive('g-slot', slot, { using: [SlotContentContext] });
75
75
  /**
76
76
  * Process native <slot> elements.
77
77
  *
package/dist/inject.d.ts CHANGED
@@ -14,9 +14,10 @@
14
14
  import type { ContextKey } from './context-registry.js';
15
15
  import type { Expression, EvalFn } from './types.js';
16
16
  /**
17
- * An injectable dependency - either a string name or a typed context key.
17
+ * An injectable dependency name.
18
+ * For ContextKey injection, use the `using` option on directive registration.
18
19
  */
19
- export type Injectable = string | ContextKey<unknown>;
20
+ export type Injectable = string;
20
21
  /**
21
22
  * Check if a value is a ContextKey.
22
23
  */
@@ -25,7 +26,7 @@ export declare function isContextKey(value: unknown): value is ContextKey<unknow
25
26
  * A function with optional `$inject` annotation.
26
27
  */
27
28
  interface InjectableFunction extends Function {
28
- $inject?: readonly Injectable[];
29
+ $inject?: readonly string[];
29
30
  }
30
31
  /**
31
32
  * Get the list of injectable dependencies for a function.
@@ -43,10 +44,12 @@ interface InjectableFunction extends Function {
43
44
  * const myDirective = (expr, ctx, el, http, userService) => {};
44
45
  * getInjectables(myDirective); // ['expr', 'ctx', 'el', 'http', 'userService']
45
46
  *
46
- * // Production - explicit annotation with context keys
47
- * myDirective.$inject = ['$element', SlotContentContext];
48
- * getInjectables(myDirective); // ['$element', SlotContentContext]
47
+ * // Production - explicit $inject array (survives minification)
48
+ * myDirective.$inject = ['$element', '$scope'];
49
+ * getInjectables(myDirective); // ['$element', '$scope']
49
50
  * ```
51
+ *
52
+ * For ContextKey injection, use the `using` option on directive registration.
50
53
  */
51
54
  export declare function getInjectables(fn: InjectableFunction): Injectable[];
52
55
  /**
package/dist/inject.js CHANGED
@@ -33,10 +33,12 @@ export function isContextKey(value) {
33
33
  * const myDirective = (expr, ctx, el, http, userService) => {};
34
34
  * getInjectables(myDirective); // ['expr', 'ctx', 'el', 'http', 'userService']
35
35
  *
36
- * // Production - explicit annotation with context keys
37
- * myDirective.$inject = ['$element', SlotContentContext];
38
- * getInjectables(myDirective); // ['$element', SlotContentContext]
36
+ * // Production - explicit $inject array (survives minification)
37
+ * myDirective.$inject = ['$element', '$scope'];
38
+ * getInjectables(myDirective); // ['$element', '$scope']
39
39
  * ```
40
+ *
41
+ * For ContextKey injection, use the `using` option on directive registration.
40
42
  */
41
43
  export function getInjectables(fn) {
42
44
  if ('$inject' in fn && Array.isArray(fn.$inject)) {
@@ -85,11 +87,6 @@ function parseFunctionParams(fn) {
85
87
  export function resolveDependencies(fn, expr, element, evalFn, config, using) {
86
88
  const inject = getInjectables(fn);
87
89
  const args = inject.map(dep => {
88
- // Handle ContextKey injection
89
- if (isContextKey(dep)) {
90
- return config.resolveContext(dep);
91
- }
92
- // Handle string-based injection
93
90
  switch (dep) {
94
91
  case '$expr':
95
92
  return expr;
@@ -7,7 +7,8 @@ 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';
10
- import { getLocalState, registerProvider, resolveFromProviders, resolveFromDIProviders } from '../providers.js';
10
+ import { registerProvider, resolveFromProviders, resolveFromDIProviders } from '../providers.js';
11
+ import { createElementScope, getElementScope } from '../scope.js';
11
12
  import { FOR_PROCESSED_ATTR, FOR_TEMPLATE_ATTR } from '../directives/for.js';
12
13
  import { IF_PROCESSED_ATTR } from '../directives/if.js';
13
14
  import { resolveDependencies as resolveInjectables } from '../inject.js';
@@ -83,15 +84,32 @@ export function registerDirective(registry, name, fn) {
83
84
  export function registerService(name, service) {
84
85
  services.set(name, service);
85
86
  }
87
+ /**
88
+ * Find the nearest scope by walking up the DOM tree.
89
+ * Falls back to rootState if no element scope found.
90
+ *
91
+ * @internal
92
+ */
93
+ function findServerScope(el, rootState) {
94
+ let current = el;
95
+ while (current) {
96
+ const scope = getElementScope(current);
97
+ if (scope) {
98
+ return scope;
99
+ }
100
+ current = current.parentElement;
101
+ }
102
+ return rootState;
103
+ }
86
104
  /**
87
105
  * Create resolver config for server-side dependency resolution.
88
106
  *
89
107
  * @internal
90
108
  */
91
- function createServerResolverConfig(el, rootState) {
109
+ function createServerResolverConfig(el, scopeState, rootState) {
92
110
  return {
93
111
  resolveContext: (key) => resolveContext(el, key),
94
- resolveState: () => getLocalState(el) ?? rootState,
112
+ resolveState: () => scopeState,
95
113
  resolveRootState: () => rootState,
96
114
  resolveCustom: (name) => {
97
115
  // Look up in ancestor DI providers first (provide option)
@@ -293,13 +311,23 @@ export async function render(html, state, registry) {
293
311
  continue;
294
312
  }
295
313
  else {
296
- const config = createServerResolverConfig(item.el, state);
314
+ // Determine scope for this directive
315
+ let scopeState;
316
+ if (item.directive.$context?.length) {
317
+ // Directives with $context get their own scope to populate
318
+ const parentScope = findServerScope(item.el, state);
319
+ scopeState = createElementScope(item.el, parentScope);
320
+ }
321
+ else {
322
+ // Other directives use nearest ancestor scope or root
323
+ scopeState = findServerScope(item.el, state);
324
+ }
325
+ const config = createServerResolverConfig(item.el, scopeState, state);
297
326
  const args = resolveInjectables(item.directive, item.expr, item.el, ctx.eval.bind(ctx), config, item.using);
298
327
  await item.directive(...args);
299
328
  // Register as context provider if directive declares $context
300
329
  if (item.directive.$context?.length) {
301
- const localState = getLocalState(item.el);
302
- registerProvider(item.el, item.directive, localState);
330
+ registerProvider(item.el, item.directive, scopeState);
303
331
  }
304
332
  }
305
333
  }
package/dist/types.d.ts CHANGED
@@ -4,7 +4,6 @@
4
4
  * @packageDocumentation
5
5
  */
6
6
  import type { ContextKey } from './context-registry.js';
7
- import type { Injectable } from './inject.js';
8
7
  /**
9
8
  * Execution mode for the framework.
10
9
  */
@@ -160,14 +159,15 @@ export interface DirectiveMeta<T = InjectableRegistry> {
160
159
  *
161
160
  * @example
162
161
  * ```ts
163
- * // String-based injection
164
162
  * myDirective.$inject = ['$element', '$scope'];
163
+ * ```
165
164
  *
166
- * // With typed context keys
167
- * myDirective.$inject = ['$element', SlotContentContext];
165
+ * For typed context keys, use the `using` option on directive registration:
166
+ * ```ts
167
+ * directive('my-directive', myDirective, { using: [SlotContentContext] });
168
168
  * ```
169
169
  */
170
- $inject?: readonly Injectable[];
170
+ $inject?: readonly string[];
171
171
  /**
172
172
  * Names this directive exposes as context to descendants.
173
173
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gonia",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "A lightweight, SSR-first reactive UI library with declarative directives",
5
5
  "type": "module",
6
6
  "license": "MIT",