gonia 0.1.2 → 0.2.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.
- package/README.md +24 -7
- package/dist/client/hydrate.js +94 -59
- package/dist/context-registry.d.ts +130 -0
- package/dist/context-registry.js +153 -0
- package/dist/directives/for.d.ts +1 -1
- package/dist/directives/for.js +12 -76
- package/dist/directives/if.d.ts +4 -1
- package/dist/directives/if.js +69 -66
- package/dist/directives/slot.d.ts +4 -2
- package/dist/directives/slot.js +11 -16
- package/dist/directives/template.d.ts +9 -2
- package/dist/directives/template.js +14 -13
- package/dist/dom.d.ts +29 -0
- package/dist/dom.js +39 -0
- package/dist/expression.js +17 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +5 -2
- package/dist/inject.d.ts +47 -6
- package/dist/inject.js +66 -4
- package/dist/process.d.ts +61 -0
- package/dist/process.js +215 -0
- package/dist/providers.js +9 -12
- package/dist/scope.js +17 -22
- package/dist/server/render.js +70 -41
- package/dist/types.d.ts +130 -20
- package/dist/types.js +48 -2
- package/dist/vite/plugin.d.ts +27 -0
- package/dist/vite/plugin.js +56 -3
- package/package.json +9 -2
package/dist/inject.d.ts
CHANGED
|
@@ -11,11 +11,21 @@
|
|
|
11
11
|
*
|
|
12
12
|
* @packageDocumentation
|
|
13
13
|
*/
|
|
14
|
+
import type { ContextKey } from './context-registry.js';
|
|
15
|
+
import type { Expression, EvalFn } from './types.js';
|
|
16
|
+
/**
|
|
17
|
+
* An injectable dependency - either a string name or a typed context key.
|
|
18
|
+
*/
|
|
19
|
+
export type Injectable = string | ContextKey<unknown>;
|
|
20
|
+
/**
|
|
21
|
+
* Check if a value is a ContextKey.
|
|
22
|
+
*/
|
|
23
|
+
export declare function isContextKey(value: unknown): value is ContextKey<unknown>;
|
|
14
24
|
/**
|
|
15
25
|
* A function with optional `$inject` annotation.
|
|
16
26
|
*/
|
|
17
27
|
interface InjectableFunction extends Function {
|
|
18
|
-
$inject?: readonly
|
|
28
|
+
$inject?: readonly Injectable[];
|
|
19
29
|
}
|
|
20
30
|
/**
|
|
21
31
|
* Get the list of injectable dependencies for a function.
|
|
@@ -25,7 +35,7 @@ interface InjectableFunction extends Function {
|
|
|
25
35
|
* In production, always use `$inject` to survive minification.
|
|
26
36
|
*
|
|
27
37
|
* @param fn - The function to inspect
|
|
28
|
-
* @returns Array of dependency names
|
|
38
|
+
* @returns Array of dependency names or context keys
|
|
29
39
|
*
|
|
30
40
|
* @example
|
|
31
41
|
* ```ts
|
|
@@ -33,10 +43,41 @@ interface InjectableFunction extends Function {
|
|
|
33
43
|
* const myDirective = (expr, ctx, el, http, userService) => {};
|
|
34
44
|
* getInjectables(myDirective); // ['expr', 'ctx', 'el', 'http', 'userService']
|
|
35
45
|
*
|
|
36
|
-
* // Production - explicit annotation
|
|
37
|
-
* myDirective.$inject = ['
|
|
38
|
-
* getInjectables(myDirective); // ['
|
|
46
|
+
* // Production - explicit annotation with context keys
|
|
47
|
+
* myDirective.$inject = ['$element', SlotContentContext];
|
|
48
|
+
* getInjectables(myDirective); // ['$element', SlotContentContext]
|
|
39
49
|
* ```
|
|
40
50
|
*/
|
|
41
|
-
export declare function getInjectables(fn: InjectableFunction):
|
|
51
|
+
export declare function getInjectables(fn: InjectableFunction): Injectable[];
|
|
52
|
+
/**
|
|
53
|
+
* Configuration for dependency resolution.
|
|
54
|
+
*/
|
|
55
|
+
export interface DependencyResolverConfig {
|
|
56
|
+
/** Resolve a ContextKey to its value */
|
|
57
|
+
resolveContext: (key: ContextKey<unknown>) => unknown;
|
|
58
|
+
/** Resolve $scope injectable */
|
|
59
|
+
resolveState: () => Record<string, unknown>;
|
|
60
|
+
/** Resolve $rootState injectable (may be same as state) */
|
|
61
|
+
resolveRootState?: () => Record<string, unknown>;
|
|
62
|
+
/** Resolve custom injectable by name (services, providers) */
|
|
63
|
+
resolveCustom?: (name: string) => unknown | undefined;
|
|
64
|
+
/** Current mode */
|
|
65
|
+
mode: 'server' | 'client';
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Resolve dependencies for a directive function.
|
|
69
|
+
*
|
|
70
|
+
* @remarks
|
|
71
|
+
* Unified dependency resolution used by both client and server.
|
|
72
|
+
* Handles $inject arrays, ContextKey injection, and the `using` option.
|
|
73
|
+
*
|
|
74
|
+
* @param fn - The directive function (with optional $inject)
|
|
75
|
+
* @param expr - The expression string from the directive attribute
|
|
76
|
+
* @param element - The target DOM element
|
|
77
|
+
* @param evalFn - Function to evaluate expressions
|
|
78
|
+
* @param config - Resolution configuration
|
|
79
|
+
* @param using - Optional array of context keys to append
|
|
80
|
+
* @returns Array of resolved dependency values
|
|
81
|
+
*/
|
|
82
|
+
export declare function resolveDependencies(fn: InjectableFunction, expr: Expression | string, element: Element, evalFn: EvalFn, config: DependencyResolverConfig, using?: ContextKey<unknown>[]): unknown[];
|
|
42
83
|
export {};
|
package/dist/inject.js
CHANGED
|
@@ -11,6 +11,12 @@
|
|
|
11
11
|
*
|
|
12
12
|
* @packageDocumentation
|
|
13
13
|
*/
|
|
14
|
+
/**
|
|
15
|
+
* Check if a value is a ContextKey.
|
|
16
|
+
*/
|
|
17
|
+
export function isContextKey(value) {
|
|
18
|
+
return typeof value === 'object' && value !== null && 'id' in value && typeof value.id === 'symbol';
|
|
19
|
+
}
|
|
14
20
|
/**
|
|
15
21
|
* Get the list of injectable dependencies for a function.
|
|
16
22
|
*
|
|
@@ -19,7 +25,7 @@
|
|
|
19
25
|
* In production, always use `$inject` to survive minification.
|
|
20
26
|
*
|
|
21
27
|
* @param fn - The function to inspect
|
|
22
|
-
* @returns Array of dependency names
|
|
28
|
+
* @returns Array of dependency names or context keys
|
|
23
29
|
*
|
|
24
30
|
* @example
|
|
25
31
|
* ```ts
|
|
@@ -27,9 +33,9 @@
|
|
|
27
33
|
* const myDirective = (expr, ctx, el, http, userService) => {};
|
|
28
34
|
* getInjectables(myDirective); // ['expr', 'ctx', 'el', 'http', 'userService']
|
|
29
35
|
*
|
|
30
|
-
* // Production - explicit annotation
|
|
31
|
-
* myDirective.$inject = ['
|
|
32
|
-
* getInjectables(myDirective); // ['
|
|
36
|
+
* // Production - explicit annotation with context keys
|
|
37
|
+
* myDirective.$inject = ['$element', SlotContentContext];
|
|
38
|
+
* getInjectables(myDirective); // ['$element', SlotContentContext]
|
|
33
39
|
* ```
|
|
34
40
|
*/
|
|
35
41
|
export function getInjectables(fn) {
|
|
@@ -61,3 +67,59 @@ function parseFunctionParams(fn) {
|
|
|
61
67
|
.map(p => p.replace(/\s*=.*$/, ''))
|
|
62
68
|
.filter(Boolean);
|
|
63
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Resolve dependencies for a directive function.
|
|
72
|
+
*
|
|
73
|
+
* @remarks
|
|
74
|
+
* Unified dependency resolution used by both client and server.
|
|
75
|
+
* Handles $inject arrays, ContextKey injection, and the `using` option.
|
|
76
|
+
*
|
|
77
|
+
* @param fn - The directive function (with optional $inject)
|
|
78
|
+
* @param expr - The expression string from the directive attribute
|
|
79
|
+
* @param element - The target DOM element
|
|
80
|
+
* @param evalFn - Function to evaluate expressions
|
|
81
|
+
* @param config - Resolution configuration
|
|
82
|
+
* @param using - Optional array of context keys to append
|
|
83
|
+
* @returns Array of resolved dependency values
|
|
84
|
+
*/
|
|
85
|
+
export function resolveDependencies(fn, expr, element, evalFn, config, using) {
|
|
86
|
+
const inject = getInjectables(fn);
|
|
87
|
+
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
|
+
switch (dep) {
|
|
94
|
+
case '$expr':
|
|
95
|
+
return expr;
|
|
96
|
+
case '$element':
|
|
97
|
+
return element;
|
|
98
|
+
case '$eval':
|
|
99
|
+
return evalFn;
|
|
100
|
+
case '$scope':
|
|
101
|
+
return config.resolveState();
|
|
102
|
+
case '$rootState':
|
|
103
|
+
return config.resolveRootState?.() ?? config.resolveState();
|
|
104
|
+
case '$mode':
|
|
105
|
+
return config.mode;
|
|
106
|
+
default: {
|
|
107
|
+
// Look up in custom resolver (services, providers, etc.)
|
|
108
|
+
if (config.resolveCustom) {
|
|
109
|
+
const resolved = config.resolveCustom(dep);
|
|
110
|
+
if (resolved !== undefined) {
|
|
111
|
+
return resolved;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
throw new Error(`Unknown injectable: ${dep}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
// Append contexts from `using` option
|
|
119
|
+
if (using?.length) {
|
|
120
|
+
for (const key of using) {
|
|
121
|
+
args.push(config.resolveContext(key));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return args;
|
|
125
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared element processing for structural directives.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Provides a unified way to process directives on elements created by
|
|
6
|
+
* structural directives like g-if and g-for. Supports scope reuse for
|
|
7
|
+
* state preservation across re-renders.
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
import { Mode } from './types.js';
|
|
12
|
+
/** Attribute used to mark elements processed by structural directives */
|
|
13
|
+
export declare const PROCESSED_ATTR = "data-g-processed";
|
|
14
|
+
/**
|
|
15
|
+
* Options for processing element directives.
|
|
16
|
+
*/
|
|
17
|
+
export interface ProcessOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Existing scope to use instead of creating a new one.
|
|
20
|
+
* Use this to preserve state across re-renders (e.g., g-if toggle).
|
|
21
|
+
*/
|
|
22
|
+
existingScope?: Record<string, unknown>;
|
|
23
|
+
/**
|
|
24
|
+
* Additional properties to add to the scope.
|
|
25
|
+
* Used by g-for to add item/index variables.
|
|
26
|
+
*/
|
|
27
|
+
scopeAdditions?: Record<string, unknown>;
|
|
28
|
+
/**
|
|
29
|
+
* Skip processing structural directives (g-for, g-if).
|
|
30
|
+
* Set to true when processing content inside a structural directive
|
|
31
|
+
* to avoid infinite recursion.
|
|
32
|
+
*/
|
|
33
|
+
skipStructural?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Process directives on an element using registered directives.
|
|
37
|
+
*
|
|
38
|
+
* @remarks
|
|
39
|
+
* This processes all non-structural directives (g-text, g-class, g-show, g-on, etc.)
|
|
40
|
+
* on an element. For structural directives, use the directives directly.
|
|
41
|
+
*
|
|
42
|
+
* @param el - The element to process
|
|
43
|
+
* @param parentScope - The parent scope for variable resolution
|
|
44
|
+
* @param mode - Server or client mode
|
|
45
|
+
* @param options - Processing options
|
|
46
|
+
* @returns The scope used for this element (for chaining/children)
|
|
47
|
+
*/
|
|
48
|
+
export declare function processElementDirectives(el: Element, parentScope: Record<string, unknown>, mode: Mode, options?: ProcessOptions): Record<string, unknown>;
|
|
49
|
+
/**
|
|
50
|
+
* Process an element tree (element and all descendants).
|
|
51
|
+
*
|
|
52
|
+
* @remarks
|
|
53
|
+
* Recursively processes directives on an element and all its children.
|
|
54
|
+
* Each child gets its own scope that inherits from the parent.
|
|
55
|
+
*
|
|
56
|
+
* @param el - The root element to process
|
|
57
|
+
* @param parentScope - The parent scope
|
|
58
|
+
* @param mode - Server or client mode
|
|
59
|
+
* @param options - Processing options (existingScope only applies to root element)
|
|
60
|
+
*/
|
|
61
|
+
export declare function processElementTree(el: Element, parentScope: Record<string, unknown>, mode: Mode, options?: ProcessOptions): void;
|
package/dist/process.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared element processing for structural directives.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Provides a unified way to process directives on elements created by
|
|
6
|
+
* structural directives like g-if and g-for. Supports scope reuse for
|
|
7
|
+
* state preservation across re-renders.
|
|
8
|
+
*
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
import { Mode, getDirective } from './types.js';
|
|
12
|
+
import { createContext } from './context.js';
|
|
13
|
+
import { createScope, effect } from './reactivity.js';
|
|
14
|
+
import { resolveDependencies } from './inject.js';
|
|
15
|
+
import { resolveContext } from './context-registry.js';
|
|
16
|
+
import { resolveFromProviders, resolveFromDIProviders } from './providers.js';
|
|
17
|
+
/** Attribute used to mark elements processed by structural directives */
|
|
18
|
+
export const PROCESSED_ATTR = 'data-g-processed';
|
|
19
|
+
/**
|
|
20
|
+
* Create resolver config for dependency resolution.
|
|
21
|
+
*
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
function createResolverConfig(el, scope, mode) {
|
|
25
|
+
return {
|
|
26
|
+
resolveContext: (key) => resolveContext(el, key),
|
|
27
|
+
resolveState: () => scope,
|
|
28
|
+
resolveRootState: () => scope,
|
|
29
|
+
resolveCustom: (name) => {
|
|
30
|
+
const diProvided = resolveFromDIProviders(el, name);
|
|
31
|
+
if (diProvided !== undefined)
|
|
32
|
+
return diProvided;
|
|
33
|
+
return resolveFromProviders(el, name);
|
|
34
|
+
},
|
|
35
|
+
mode: mode === Mode.SERVER ? 'server' : 'client'
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Set up an event handler on an element.
|
|
40
|
+
*
|
|
41
|
+
* @internal
|
|
42
|
+
*/
|
|
43
|
+
function setupEventHandler(el, expr, ctx, scope) {
|
|
44
|
+
const colonIdx = expr.indexOf(':');
|
|
45
|
+
if (colonIdx === -1) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const eventName = expr.slice(0, colonIdx).trim();
|
|
49
|
+
const handlerExpr = expr.slice(colonIdx + 1).trim();
|
|
50
|
+
el.addEventListener(eventName, (event) => {
|
|
51
|
+
const result = ctx.eval(handlerExpr);
|
|
52
|
+
if (typeof result === 'function') {
|
|
53
|
+
result.call(scope, event);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Process directives on an element using registered directives.
|
|
59
|
+
*
|
|
60
|
+
* @remarks
|
|
61
|
+
* This processes all non-structural directives (g-text, g-class, g-show, g-on, etc.)
|
|
62
|
+
* on an element. For structural directives, use the directives directly.
|
|
63
|
+
*
|
|
64
|
+
* @param el - The element to process
|
|
65
|
+
* @param parentScope - The parent scope for variable resolution
|
|
66
|
+
* @param mode - Server or client mode
|
|
67
|
+
* @param options - Processing options
|
|
68
|
+
* @returns The scope used for this element (for chaining/children)
|
|
69
|
+
*/
|
|
70
|
+
export function processElementDirectives(el, parentScope, mode, options = {}) {
|
|
71
|
+
const { existingScope, scopeAdditions = {}, skipStructural = true } = options;
|
|
72
|
+
// Mark element as processed
|
|
73
|
+
el.setAttribute(PROCESSED_ATTR, '');
|
|
74
|
+
// Use existing scope or create a new child scope
|
|
75
|
+
const scope = existingScope
|
|
76
|
+
? (Object.keys(scopeAdditions).length > 0
|
|
77
|
+
? createScope(existingScope, scopeAdditions)
|
|
78
|
+
: existingScope)
|
|
79
|
+
: createScope(parentScope, scopeAdditions);
|
|
80
|
+
const ctx = createContext(mode, scope);
|
|
81
|
+
// Process g-scope (inline scope initialization)
|
|
82
|
+
const scopeAttr = el.getAttribute('g-scope');
|
|
83
|
+
if (scopeAttr) {
|
|
84
|
+
const scopeValues = ctx.eval(scopeAttr);
|
|
85
|
+
if (scopeValues && typeof scopeValues === 'object') {
|
|
86
|
+
Object.assign(scope, scopeValues);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Process g-text
|
|
90
|
+
const textAttr = el.getAttribute('g-text');
|
|
91
|
+
if (textAttr) {
|
|
92
|
+
if (mode === Mode.CLIENT) {
|
|
93
|
+
effect(() => {
|
|
94
|
+
const value = ctx.eval(textAttr);
|
|
95
|
+
el.textContent = String(value ?? '');
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
const value = ctx.eval(textAttr);
|
|
100
|
+
el.textContent = String(value ?? '');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Process g-class
|
|
104
|
+
const classAttr = el.getAttribute('g-class');
|
|
105
|
+
if (classAttr) {
|
|
106
|
+
const applyClasses = () => {
|
|
107
|
+
const classObj = ctx.eval(classAttr);
|
|
108
|
+
if (classObj && typeof classObj === 'object') {
|
|
109
|
+
for (const [className, shouldAdd] of Object.entries(classObj)) {
|
|
110
|
+
if (shouldAdd) {
|
|
111
|
+
el.classList.add(className);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
el.classList.remove(className);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
if (mode === Mode.CLIENT) {
|
|
120
|
+
effect(applyClasses);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
applyClasses();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Process g-show
|
|
127
|
+
const showAttr = el.getAttribute('g-show');
|
|
128
|
+
if (showAttr) {
|
|
129
|
+
const applyShow = () => {
|
|
130
|
+
const value = ctx.eval(showAttr);
|
|
131
|
+
el.style.display = value ? '' : 'none';
|
|
132
|
+
};
|
|
133
|
+
if (mode === Mode.CLIENT) {
|
|
134
|
+
effect(applyShow);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
applyShow();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Process g-on (client only)
|
|
141
|
+
if (mode === Mode.CLIENT) {
|
|
142
|
+
const onAttr = el.getAttribute('g-on');
|
|
143
|
+
if (onAttr) {
|
|
144
|
+
setupEventHandler(el, onAttr, ctx, scope);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Process g-model (client only)
|
|
148
|
+
if (mode === Mode.CLIENT) {
|
|
149
|
+
const modelAttr = el.getAttribute('g-model');
|
|
150
|
+
if (modelAttr) {
|
|
151
|
+
const registration = getDirective('g-model');
|
|
152
|
+
if (registration?.fn) {
|
|
153
|
+
const config = createResolverConfig(el, scope, mode);
|
|
154
|
+
const args = resolveDependencies(registration.fn, modelAttr, el, ctx.eval.bind(ctx), config, registration.options.using);
|
|
155
|
+
registration.fn(...args);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Process g-html
|
|
160
|
+
const htmlAttr = el.getAttribute('g-html');
|
|
161
|
+
if (htmlAttr) {
|
|
162
|
+
const applyHtml = () => {
|
|
163
|
+
const value = ctx.eval(htmlAttr);
|
|
164
|
+
el.innerHTML = String(value ?? '');
|
|
165
|
+
};
|
|
166
|
+
if (mode === Mode.CLIENT) {
|
|
167
|
+
effect(applyHtml);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
applyHtml();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Process g-bind:* attributes
|
|
174
|
+
const bindAttrs = [...el.attributes].filter(a => a.name.startsWith('g-bind:'));
|
|
175
|
+
for (const attr of bindAttrs) {
|
|
176
|
+
const targetAttr = attr.name.slice('g-bind:'.length);
|
|
177
|
+
const valueExpr = attr.value;
|
|
178
|
+
const applyBinding = () => {
|
|
179
|
+
const value = ctx.eval(valueExpr);
|
|
180
|
+
if (value === null || value === undefined) {
|
|
181
|
+
el.removeAttribute(targetAttr);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
el.setAttribute(targetAttr, String(value));
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
if (mode === Mode.CLIENT) {
|
|
188
|
+
effect(applyBinding);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
applyBinding();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return scope;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Process an element tree (element and all descendants).
|
|
198
|
+
*
|
|
199
|
+
* @remarks
|
|
200
|
+
* Recursively processes directives on an element and all its children.
|
|
201
|
+
* Each child gets its own scope that inherits from the parent.
|
|
202
|
+
*
|
|
203
|
+
* @param el - The root element to process
|
|
204
|
+
* @param parentScope - The parent scope
|
|
205
|
+
* @param mode - Server or client mode
|
|
206
|
+
* @param options - Processing options (existingScope only applies to root element)
|
|
207
|
+
*/
|
|
208
|
+
export function processElementTree(el, parentScope, mode, options = {}) {
|
|
209
|
+
// Process the root element
|
|
210
|
+
const scope = processElementDirectives(el, parentScope, mode, options);
|
|
211
|
+
// Process children recursively (they get fresh child scopes)
|
|
212
|
+
for (const child of el.children) {
|
|
213
|
+
processElementTree(child, scope, mode, { skipStructural: true });
|
|
214
|
+
}
|
|
215
|
+
}
|
package/dist/providers.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* @packageDocumentation
|
|
5
5
|
*/
|
|
6
6
|
import { reactive } from './reactivity.js';
|
|
7
|
+
import { findAncestor } from './dom.js';
|
|
7
8
|
/**
|
|
8
9
|
* Local state stored per element.
|
|
9
10
|
*
|
|
@@ -83,15 +84,13 @@ export function registerDIProviders(el, provideMap) {
|
|
|
83
84
|
* @internal
|
|
84
85
|
*/
|
|
85
86
|
export function resolveFromDIProviders(el, name) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const provideMap = diProviders.get(current);
|
|
87
|
+
return findAncestor(el, (e) => {
|
|
88
|
+
const provideMap = diProviders.get(e);
|
|
89
89
|
if (provideMap && name in provideMap) {
|
|
90
90
|
return provideMap[name];
|
|
91
91
|
}
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
return undefined;
|
|
92
|
+
return undefined;
|
|
93
|
+
});
|
|
95
94
|
}
|
|
96
95
|
/**
|
|
97
96
|
* Resolve a context value from ancestor elements.
|
|
@@ -107,15 +106,13 @@ export function resolveFromDIProviders(el, name) {
|
|
|
107
106
|
* @internal
|
|
108
107
|
*/
|
|
109
108
|
export function resolveFromProviders(el, name) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const info = contextProviders.get(current);
|
|
109
|
+
return findAncestor(el, (e) => {
|
|
110
|
+
const info = contextProviders.get(e);
|
|
113
111
|
if (info?.directive.$context?.includes(name)) {
|
|
114
112
|
return info.state;
|
|
115
113
|
}
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
return undefined;
|
|
114
|
+
return undefined;
|
|
115
|
+
});
|
|
119
116
|
}
|
|
120
117
|
/**
|
|
121
118
|
* Clear local state for an element.
|
package/dist/scope.js
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
import { reactive } from './reactivity.js';
|
|
7
7
|
import { createContext } from './context.js';
|
|
8
8
|
import { Mode } from './types.js';
|
|
9
|
-
import {
|
|
9
|
+
import { resolveDependencies } from './inject.js';
|
|
10
|
+
import { findAncestor } from './dom.js';
|
|
11
|
+
import { resolveContext } from './context-registry.js';
|
|
10
12
|
/** WeakMap to store element scopes */
|
|
11
13
|
const elementScopes = new WeakMap();
|
|
12
14
|
/** Root scope for top-level directives without explicit parent scope */
|
|
@@ -73,13 +75,9 @@ export function getElementScope(el) {
|
|
|
73
75
|
* @returns The nearest scope, or root scope if none found and fallback enabled
|
|
74
76
|
*/
|
|
75
77
|
export function findParentScope(el, includeSelf = false, useRootFallback = true) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (scope) {
|
|
80
|
-
return scope;
|
|
81
|
-
}
|
|
82
|
-
current = current.parentElement;
|
|
78
|
+
const scope = findAncestor(el, (e) => elementScopes.get(e), includeSelf);
|
|
79
|
+
if (scope) {
|
|
80
|
+
return scope;
|
|
83
81
|
}
|
|
84
82
|
// Fall back to root scope for top-level directives
|
|
85
83
|
return useRootFallback ? getRootScope() : undefined;
|
|
@@ -112,22 +110,19 @@ fn, options) {
|
|
|
112
110
|
const parentScope = findParentScope(this);
|
|
113
111
|
// Create this element's scope
|
|
114
112
|
const scope = createElementScope(this, parentScope);
|
|
113
|
+
// Apply assigned values to scope
|
|
114
|
+
if (options.assign) {
|
|
115
|
+
Object.assign(scope, options.assign);
|
|
116
|
+
}
|
|
115
117
|
// Create context for expression evaluation
|
|
116
118
|
const ctx = createContext(Mode.CLIENT, scope);
|
|
117
|
-
// Resolve dependencies
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return scope;
|
|
125
|
-
case '$eval':
|
|
126
|
-
return ctx.eval.bind(ctx);
|
|
127
|
-
default:
|
|
128
|
-
return undefined;
|
|
129
|
-
}
|
|
130
|
-
});
|
|
119
|
+
// Resolve dependencies using shared resolver
|
|
120
|
+
const config = {
|
|
121
|
+
resolveContext: (key) => resolveContext(this, key),
|
|
122
|
+
resolveState: () => scope,
|
|
123
|
+
mode: 'client'
|
|
124
|
+
};
|
|
125
|
+
const args = resolveDependencies(fn, '', this, ctx.eval.bind(ctx), config, options.using);
|
|
131
126
|
const result = fn(...args);
|
|
132
127
|
// Handle async directives
|
|
133
128
|
if (result instanceof Promise) {
|