gonia 0.0.1

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.
Files changed (54) hide show
  1. package/README.md +119 -0
  2. package/dist/client/hydrate.d.ts +54 -0
  3. package/dist/client/hydrate.js +445 -0
  4. package/dist/client/index.d.ts +7 -0
  5. package/dist/client/index.js +6 -0
  6. package/dist/context.d.ts +40 -0
  7. package/dist/context.js +69 -0
  8. package/dist/directives/class.d.ts +21 -0
  9. package/dist/directives/class.js +42 -0
  10. package/dist/directives/for.d.ts +29 -0
  11. package/dist/directives/for.js +265 -0
  12. package/dist/directives/html.d.ts +16 -0
  13. package/dist/directives/html.js +19 -0
  14. package/dist/directives/if.d.ts +25 -0
  15. package/dist/directives/if.js +133 -0
  16. package/dist/directives/index.d.ts +15 -0
  17. package/dist/directives/index.js +15 -0
  18. package/dist/directives/model.d.ts +27 -0
  19. package/dist/directives/model.js +134 -0
  20. package/dist/directives/on.d.ts +21 -0
  21. package/dist/directives/on.js +54 -0
  22. package/dist/directives/show.d.ts +15 -0
  23. package/dist/directives/show.js +19 -0
  24. package/dist/directives/slot.d.ts +48 -0
  25. package/dist/directives/slot.js +99 -0
  26. package/dist/directives/template.d.ts +55 -0
  27. package/dist/directives/template.js +147 -0
  28. package/dist/directives/text.d.ts +15 -0
  29. package/dist/directives/text.js +18 -0
  30. package/dist/expression.d.ts +60 -0
  31. package/dist/expression.js +96 -0
  32. package/dist/index.d.ts +19 -0
  33. package/dist/index.js +16 -0
  34. package/dist/inject.d.ts +42 -0
  35. package/dist/inject.js +63 -0
  36. package/dist/providers.d.ts +96 -0
  37. package/dist/providers.js +146 -0
  38. package/dist/reactivity.d.ts +95 -0
  39. package/dist/reactivity.js +219 -0
  40. package/dist/scope.d.ts +43 -0
  41. package/dist/scope.js +112 -0
  42. package/dist/server/index.d.ts +7 -0
  43. package/dist/server/index.js +6 -0
  44. package/dist/server/render.d.ts +61 -0
  45. package/dist/server/render.js +243 -0
  46. package/dist/templates.d.ts +92 -0
  47. package/dist/templates.js +124 -0
  48. package/dist/types.d.ts +362 -0
  49. package/dist/types.js +110 -0
  50. package/dist/vite/index.d.ts +6 -0
  51. package/dist/vite/index.js +6 -0
  52. package/dist/vite/plugin.d.ts +30 -0
  53. package/dist/vite/plugin.js +127 -0
  54. package/package.json +67 -0
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Template registry for reusable DOM.
3
+ *
4
+ * @remarks
5
+ * Templates are just reusable HTML. The registry provides async access
6
+ * to templates from various sources: inline `<template>` tags, fetched
7
+ * files, or custom sources.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ /**
12
+ * Interface for template retrieval.
13
+ *
14
+ * @remarks
15
+ * Implementations can source templates from anywhere:
16
+ * inline tags, files, network, bundled, etc.
17
+ */
18
+ export interface TemplateRegistry {
19
+ /**
20
+ * Get a template by name.
21
+ *
22
+ * @param name - Template name/path
23
+ * @returns The template HTML string
24
+ */
25
+ get(name: string): Promise<string>;
26
+ }
27
+ /**
28
+ * Create a template registry with inline -> fetch fallback.
29
+ *
30
+ * @remarks
31
+ * First checks for a `<template id="{name}">` in the document.
32
+ * If not found, fetches from `/{name}` (or custom base path).
33
+ * Results are cached.
34
+ *
35
+ * @param options - Configuration options
36
+ * @returns A template registry
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * // Default: inline -> fetch from /
41
+ * const templates = createTemplateRegistry();
42
+ *
43
+ * // Custom base path
44
+ * const templates = createTemplateRegistry({ basePath: '/templates/' });
45
+ *
46
+ * // Usage
47
+ * const html = await templates.get('dialog');
48
+ * ```
49
+ */
50
+ export declare function createTemplateRegistry(options?: {
51
+ basePath?: string;
52
+ fetch?: typeof globalThis.fetch;
53
+ }): TemplateRegistry;
54
+ /**
55
+ * Create a simple in-memory template registry.
56
+ *
57
+ * @remarks
58
+ * Useful for testing or when templates are bundled.
59
+ *
60
+ * @param templates - Map of template names to HTML
61
+ * @returns A template registry
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * const templates = createMemoryRegistry({
66
+ * dialog: '<div class="dialog"><slot></slot></div>',
67
+ * card: '<div class="card"><slot name="title"></slot><slot></slot></div>'
68
+ * });
69
+ * ```
70
+ */
71
+ export declare function createMemoryRegistry(templates: Record<string, string>): TemplateRegistry;
72
+ /**
73
+ * Create a server-side template registry that reads from filesystem.
74
+ *
75
+ * @remarks
76
+ * For Node.js/server environments. Reads templates from disk.
77
+ *
78
+ * @param readFile - Async file reader function
79
+ * @param basePath - Base directory path
80
+ * @returns A template registry
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * import { readFile } from 'fs/promises';
85
+ *
86
+ * const templates = createServerRegistry(
87
+ * (path) => readFile(path, 'utf-8'),
88
+ * './templates/'
89
+ * );
90
+ * ```
91
+ */
92
+ export declare function createServerRegistry(readFile: (path: string) => Promise<string>, basePath?: string): TemplateRegistry;
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Template registry for reusable DOM.
3
+ *
4
+ * @remarks
5
+ * Templates are just reusable HTML. The registry provides async access
6
+ * to templates from various sources: inline `<template>` tags, fetched
7
+ * files, or custom sources.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ /**
12
+ * Create a template registry with inline -> fetch fallback.
13
+ *
14
+ * @remarks
15
+ * First checks for a `<template id="{name}">` in the document.
16
+ * If not found, fetches from `/{name}` (or custom base path).
17
+ * Results are cached.
18
+ *
19
+ * @param options - Configuration options
20
+ * @returns A template registry
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * // Default: inline -> fetch from /
25
+ * const templates = createTemplateRegistry();
26
+ *
27
+ * // Custom base path
28
+ * const templates = createTemplateRegistry({ basePath: '/templates/' });
29
+ *
30
+ * // Usage
31
+ * const html = await templates.get('dialog');
32
+ * ```
33
+ */
34
+ export function createTemplateRegistry(options = {}) {
35
+ const { basePath = '/', fetch: fetchFn = globalThis.fetch } = options;
36
+ const cache = new Map();
37
+ return {
38
+ async get(name) {
39
+ if (cache.has(name)) {
40
+ return cache.get(name);
41
+ }
42
+ // Try inline <template> first
43
+ if (typeof document !== 'undefined') {
44
+ const el = document.getElementById(name);
45
+ if (el instanceof HTMLTemplateElement) {
46
+ const html = el.innerHTML;
47
+ cache.set(name, html);
48
+ return html;
49
+ }
50
+ }
51
+ // Fall back to fetch
52
+ const url = basePath + name;
53
+ const response = await fetchFn(url);
54
+ if (!response.ok) {
55
+ throw new Error(`Template not found: ${name} (${response.status})`);
56
+ }
57
+ const html = await response.text();
58
+ cache.set(name, html);
59
+ return html;
60
+ }
61
+ };
62
+ }
63
+ /**
64
+ * Create a simple in-memory template registry.
65
+ *
66
+ * @remarks
67
+ * Useful for testing or when templates are bundled.
68
+ *
69
+ * @param templates - Map of template names to HTML
70
+ * @returns A template registry
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * const templates = createMemoryRegistry({
75
+ * dialog: '<div class="dialog"><slot></slot></div>',
76
+ * card: '<div class="card"><slot name="title"></slot><slot></slot></div>'
77
+ * });
78
+ * ```
79
+ */
80
+ export function createMemoryRegistry(templates) {
81
+ return {
82
+ async get(name) {
83
+ const html = templates[name];
84
+ if (html === undefined) {
85
+ throw new Error(`Template not found: ${name}`);
86
+ }
87
+ return html;
88
+ }
89
+ };
90
+ }
91
+ /**
92
+ * Create a server-side template registry that reads from filesystem.
93
+ *
94
+ * @remarks
95
+ * For Node.js/server environments. Reads templates from disk.
96
+ *
97
+ * @param readFile - Async file reader function
98
+ * @param basePath - Base directory path
99
+ * @returns A template registry
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * import { readFile } from 'fs/promises';
104
+ *
105
+ * const templates = createServerRegistry(
106
+ * (path) => readFile(path, 'utf-8'),
107
+ * './templates/'
108
+ * );
109
+ * ```
110
+ */
111
+ export function createServerRegistry(readFile, basePath = './templates/') {
112
+ const cache = new Map();
113
+ return {
114
+ async get(name) {
115
+ if (cache.has(name)) {
116
+ return cache.get(name);
117
+ }
118
+ const path = basePath + name + '.html';
119
+ const html = await readFile(path);
120
+ cache.set(name, html);
121
+ return html;
122
+ }
123
+ };
124
+ }
@@ -0,0 +1,362 @@
1
+ /**
2
+ * Core types for gonia.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ /**
7
+ * Execution mode for the framework.
8
+ */
9
+ export declare enum Mode {
10
+ /** Server-side rendering */
11
+ SERVER = "server",
12
+ /** Client-side hydration/runtime */
13
+ CLIENT = "client"
14
+ }
15
+ declare const __brand: unique symbol;
16
+ /**
17
+ * A branded string type for expressions.
18
+ *
19
+ * @remarks
20
+ * This prevents arbitrary strings from being passed as expressions,
21
+ * reducing the risk of eval injection. Only the framework's parser
22
+ * should create Expression values.
23
+ */
24
+ export type Expression = string & {
25
+ [__brand]: 'expression';
26
+ };
27
+ /**
28
+ * Function to evaluate an expression against state.
29
+ */
30
+ export type EvalFn = <T = unknown>(expr: Expression) => T;
31
+ /**
32
+ * Registry of framework-provided injectables.
33
+ *
34
+ * @remarks
35
+ * For provider contexts, use the second type parameter of `Directive` instead:
36
+ *
37
+ * ```ts
38
+ * const themed: Directive<['$element', 'theme'], {theme: ThemeContext}> = ($el, theme) => {
39
+ * // theme is typed as ThemeContext
40
+ * };
41
+ * ```
42
+ */
43
+ export interface InjectableRegistry {
44
+ /** The expression string from the directive attribute */
45
+ $expr: Expression;
46
+ /** The target DOM element */
47
+ $element: Element;
48
+ /** Function to evaluate expressions against state */
49
+ $eval: EvalFn;
50
+ /** Local reactive state object (isolated per element) */
51
+ $state: Record<string, unknown>;
52
+ /** Root reactive state object (shared across all elements) */
53
+ $rootState: Record<string, unknown>;
54
+ /** Template registry for c-template directive */
55
+ $templates: {
56
+ get(name: string): Promise<string>;
57
+ };
58
+ /** Current execution mode (server or client) */
59
+ $mode: Mode;
60
+ }
61
+ /**
62
+ * Maps a tuple of injectable names to their corresponding types.
63
+ *
64
+ * @typeParam K - Tuple of injectable name strings
65
+ * @typeParam T - Type map (defaults to InjectableRegistry)
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * type Args = MapInjectables<['$element', '$state']>;
70
+ * // => [Element, Record<string, unknown>]
71
+ *
72
+ * // With custom type map
73
+ * type Args2 = MapInjectables<['$element', 'custom'], { $element: Element; custom: MyType }>;
74
+ * // => [Element, MyType]
75
+ * ```
76
+ */
77
+ export type MapInjectables<K extends readonly string[], T = InjectableRegistry> = {
78
+ [I in keyof K]: K[I] extends keyof T ? T[K[I]] : unknown;
79
+ };
80
+ /**
81
+ * Evaluation context passed to directives.
82
+ */
83
+ export interface Context {
84
+ /** Current execution mode */
85
+ readonly mode: Mode;
86
+ /**
87
+ * Evaluate an expression against the current state.
88
+ *
89
+ * @typeParam T - Expected return type
90
+ * @param expr - The expression to evaluate
91
+ * @returns The evaluated result
92
+ */
93
+ eval<T = unknown>(expr: Expression): T;
94
+ /**
95
+ * Get a value from the context by key.
96
+ *
97
+ * @param key - The key to look up
98
+ * @returns The value, or undefined
99
+ */
100
+ get<T = unknown>(key: string): T | undefined;
101
+ /**
102
+ * Create a child context with additional values.
103
+ *
104
+ * @param additions - Values to add to the child context
105
+ * @returns A new child context
106
+ */
107
+ child(additions: Record<string, unknown>): Context;
108
+ }
109
+ /**
110
+ * Directive priority levels.
111
+ *
112
+ * @remarks
113
+ * Higher priority directives run first. Structural directives
114
+ * (like c-if, c-for) need to run before behavioral ones.
115
+ */
116
+ export declare enum DirectivePriority {
117
+ /** Structural directives that control DOM presence (c-if, c-for) */
118
+ STRUCTURAL = 1000,
119
+ /** Template/transclusion directives */
120
+ TEMPLATE = 500,
121
+ /** Normal behavioral directives */
122
+ NORMAL = 0
123
+ }
124
+ /**
125
+ * Static metadata for a directive.
126
+ *
127
+ * @remarks
128
+ * Declared on the directive function before registration.
129
+ * Used to determine processing order and behavior.
130
+ *
131
+ * @typeParam T - Type map for injectable dependencies
132
+ */
133
+ export interface DirectiveMeta<T = InjectableRegistry> {
134
+ /**
135
+ * Whether this directive transcludes content.
136
+ *
137
+ * @remarks
138
+ * If true, children are saved before the directive runs.
139
+ */
140
+ transclude?: boolean;
141
+ /**
142
+ * Dependencies to inject into the directive.
143
+ *
144
+ * @remarks
145
+ * Available injectables:
146
+ * - `$expr`: The expression string from the attribute
147
+ * - `$element`: The target DOM element
148
+ * - `$eval`: Function to evaluate expressions: `(expr) => value`
149
+ * - `$state`: Local reactive state object (isolated per element)
150
+ * - Any registered service names
151
+ * - Any names provided by ancestor directives via `$context`
152
+ */
153
+ $inject?: readonly string[];
154
+ /**
155
+ * Names this directive exposes as context to descendants.
156
+ *
157
+ * @remarks
158
+ * When a directive declares `$context`, its `$state` becomes
159
+ * available to descendant directives under those names.
160
+ * Useful for passing state through isolate scope boundaries.
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * const themeProvider: Directive = ($state) => {
165
+ * $state.mode = 'dark';
166
+ * };
167
+ * themeProvider.$inject = ['$state'];
168
+ * themeProvider.$context = ['theme'];
169
+ *
170
+ * // Descendants can inject 'theme'
171
+ * const button: Directive = ($element, theme) => {
172
+ * console.log(theme.mode);
173
+ * };
174
+ * button.$inject = ['$element', 'theme'];
175
+ * ```
176
+ */
177
+ $context?: string[];
178
+ /**
179
+ * Processing priority. Higher runs first.
180
+ *
181
+ * @defaultValue DirectivePriority.NORMAL
182
+ */
183
+ priority?: number;
184
+ }
185
+ /**
186
+ * A directive function with typed parameters based on injectable keys.
187
+ *
188
+ * @remarks
189
+ * Use this type annotation to get contextual typing for directive parameters.
190
+ * The tuple of keys maps to parameter types from InjectableRegistry.
191
+ *
192
+ * @typeParam K - Tuple of injectable key names
193
+ * @typeParam T - Optional custom type map to extend InjectableRegistry
194
+ *
195
+ * @example
196
+ * ```ts
197
+ * // Basic usage - $element is typed as Element
198
+ * const myDirective: Directive<['$element']> = ($element) => {
199
+ * $element.textContent = 'hello';
200
+ * };
201
+ *
202
+ * // Multiple injectables
203
+ * const text: Directive<['$expr', '$element', '$eval']> = ($expr, $element, $eval) => {
204
+ * $element.textContent = String($eval($expr) ?? '');
205
+ * };
206
+ *
207
+ * // With custom types (extend InjectableRegistry first)
208
+ * declare module 'gonia' {
209
+ * interface InjectableRegistry {
210
+ * myService: { doThing(): void };
211
+ * }
212
+ * }
213
+ * const custom: Directive<['$element', 'myService']> = ($el, svc) => {
214
+ * svc.doThing();
215
+ * };
216
+ * ```
217
+ */
218
+ export type Directive<K extends readonly (string & keyof (InjectableRegistry & T))[] = readonly (string & keyof InjectableRegistry)[], T extends Record<string, unknown> = {}> = ((...args: MapInjectables<K, InjectableRegistry & T>) => void | Promise<void>) & DirectiveMeta<InjectableRegistry & T>;
219
+ /**
220
+ * Attributes passed to template functions.
221
+ */
222
+ export interface TemplateAttrs {
223
+ /** The element's innerHTML before transformation */
224
+ children: string;
225
+ /** All attributes from the element */
226
+ [attr: string]: string;
227
+ }
228
+ /**
229
+ * Template can be a string or a function that receives element attributes and the element.
230
+ */
231
+ export type TemplateOption = string | ((attrs: TemplateAttrs, el: Element) => string | Promise<string>);
232
+ /**
233
+ * Options for directive registration.
234
+ */
235
+ export interface DirectiveOptions {
236
+ /**
237
+ * Whether to create a new scope for this directive.
238
+ *
239
+ * @remarks
240
+ * When true, the directive creates a new scope that inherits
241
+ * from the parent scope via prototype chain.
242
+ *
243
+ * @defaultValue false
244
+ */
245
+ scope?: boolean;
246
+ /**
247
+ * Template for the directive's content.
248
+ *
249
+ * @remarks
250
+ * Can be a string or a function that receives the element's
251
+ * attributes and children, returning HTML.
252
+ *
253
+ * @example
254
+ * ```ts
255
+ * // Static template
256
+ * directive('my-modal', handler, {
257
+ * template: '<div class="modal"><slot></slot></div>'
258
+ * });
259
+ *
260
+ * // Dynamic template with props
261
+ * directive('fancy-heading', null, {
262
+ * template: ({ level, children }) => `<h${level}>${children}</h${level}>`
263
+ * });
264
+ *
265
+ * // Async template (e.g., dynamic import)
266
+ * directive('lazy-component', handler, {
267
+ * template: () => import('./template.html?raw').then(m => m.default)
268
+ * });
269
+ * ```
270
+ */
271
+ template?: TemplateOption;
272
+ /**
273
+ * DI provider overrides for descendants.
274
+ *
275
+ * @remarks
276
+ * Maps injectable names to values. Descendants requesting these
277
+ * names via `$inject` will receive the provided values instead
278
+ * of global services.
279
+ *
280
+ * Useful for testing (mock services) or scoping services to a subtree.
281
+ *
282
+ * @example
283
+ * ```ts
284
+ * // Test harness with mock services
285
+ * directive('test-harness', null, {
286
+ * scope: true,
287
+ * provide: {
288
+ * '$http': mockHttpClient,
289
+ * 'apiUrl': 'http://localhost:3000/test'
290
+ * }
291
+ * });
292
+ *
293
+ * // Descendants get mock values
294
+ * directive('api-consumer', ($http, apiUrl) => {
295
+ * $http.get(apiUrl + '/users');
296
+ * });
297
+ * ```
298
+ */
299
+ provide?: Record<string, unknown>;
300
+ }
301
+ /** Registered directive with options */
302
+ export interface DirectiveRegistration {
303
+ fn: Directive<any> | null;
304
+ options: DirectiveOptions;
305
+ }
306
+ /**
307
+ * Register a directive by name.
308
+ *
309
+ * @remarks
310
+ * Directives are functions with `$inject` set to declare dependencies.
311
+ * If the name contains a hyphen and scope is true, it's also registered
312
+ * as a custom element.
313
+ *
314
+ * The function can be `null` for pure template directives that have no
315
+ * runtime behavior.
316
+ *
317
+ * @param name - The directive name (e.g., 'c-text' or 'todo-app')
318
+ * @param fn - The directive function, or null for template-only directives
319
+ * @param options - Registration options
320
+ *
321
+ * @example
322
+ * ```ts
323
+ * // Directive with behavior
324
+ * directive('todo-app', ($element, $state) => {
325
+ * $state.todos = [];
326
+ * }, { scope: true });
327
+ *
328
+ * // Template-only directive
329
+ * directive('fancy-heading', null, {
330
+ * template: ({ level, children }) => `<h${level}>${children}</h${level}>`
331
+ * });
332
+ * ```
333
+ */
334
+ export declare function directive(name: string, fn: Directive<any> | null, options?: DirectiveOptions): void;
335
+ /**
336
+ * Get a directive registration by name.
337
+ *
338
+ * @param name - The directive name
339
+ * @returns The directive registration (fn and options), or undefined if not found
340
+ */
341
+ export declare function getDirective(name: string): DirectiveRegistration | undefined;
342
+ /**
343
+ * Get a directive function by name.
344
+ *
345
+ * @param name - The directive name
346
+ * @returns The directive function, null for template-only directives, or undefined if not found
347
+ */
348
+ export declare function getDirectiveFn(name: string): Directive<any> | null | undefined;
349
+ /**
350
+ * Get all registered directive names.
351
+ *
352
+ * @returns Array of directive names
353
+ */
354
+ export declare function getDirectiveNames(): string[];
355
+ /**
356
+ * Clear all registered directives.
357
+ *
358
+ * @remarks
359
+ * Primarily useful for testing.
360
+ */
361
+ export declare function clearDirectives(): void;
362
+ export {};
package/dist/types.js ADDED
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Core types for gonia.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ /**
7
+ * Execution mode for the framework.
8
+ */
9
+ export var Mode;
10
+ (function (Mode) {
11
+ /** Server-side rendering */
12
+ Mode["SERVER"] = "server";
13
+ /** Client-side hydration/runtime */
14
+ Mode["CLIENT"] = "client";
15
+ })(Mode || (Mode = {}));
16
+ /**
17
+ * Directive priority levels.
18
+ *
19
+ * @remarks
20
+ * Higher priority directives run first. Structural directives
21
+ * (like c-if, c-for) need to run before behavioral ones.
22
+ */
23
+ export var DirectivePriority;
24
+ (function (DirectivePriority) {
25
+ /** Structural directives that control DOM presence (c-if, c-for) */
26
+ DirectivePriority[DirectivePriority["STRUCTURAL"] = 1000] = "STRUCTURAL";
27
+ /** Template/transclusion directives */
28
+ DirectivePriority[DirectivePriority["TEMPLATE"] = 500] = "TEMPLATE";
29
+ /** Normal behavioral directives */
30
+ DirectivePriority[DirectivePriority["NORMAL"] = 0] = "NORMAL";
31
+ })(DirectivePriority || (DirectivePriority = {}));
32
+ /** Global directive registry */
33
+ const directiveRegistry = new Map();
34
+ /**
35
+ * Register a directive by name.
36
+ *
37
+ * @remarks
38
+ * Directives are functions with `$inject` set to declare dependencies.
39
+ * If the name contains a hyphen and scope is true, it's also registered
40
+ * as a custom element.
41
+ *
42
+ * The function can be `null` for pure template directives that have no
43
+ * runtime behavior.
44
+ *
45
+ * @param name - The directive name (e.g., 'c-text' or 'todo-app')
46
+ * @param fn - The directive function, or null for template-only directives
47
+ * @param options - Registration options
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * // Directive with behavior
52
+ * directive('todo-app', ($element, $state) => {
53
+ * $state.todos = [];
54
+ * }, { scope: true });
55
+ *
56
+ * // Template-only directive
57
+ * directive('fancy-heading', null, {
58
+ * template: ({ level, children }) => `<h${level}>${children}</h${level}>`
59
+ * });
60
+ * ```
61
+ */
62
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
+ export function directive(name, fn, options = {}) {
64
+ directiveRegistry.set(name, { fn, options });
65
+ // Register as custom element if name contains hyphen and scope is true
66
+ if (fn && name.includes('-') && options.scope && typeof customElements !== 'undefined') {
67
+ // Defer to avoid circular deps - custom element class is created in scope.ts
68
+ queueMicrotask(() => {
69
+ import('./scope.js').then(({ registerDirectiveElement }) => {
70
+ registerDirectiveElement(name, fn, options);
71
+ });
72
+ });
73
+ }
74
+ }
75
+ /**
76
+ * Get a directive registration by name.
77
+ *
78
+ * @param name - The directive name
79
+ * @returns The directive registration (fn and options), or undefined if not found
80
+ */
81
+ export function getDirective(name) {
82
+ return directiveRegistry.get(name);
83
+ }
84
+ /**
85
+ * Get a directive function by name.
86
+ *
87
+ * @param name - The directive name
88
+ * @returns The directive function, null for template-only directives, or undefined if not found
89
+ */
90
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
91
+ export function getDirectiveFn(name) {
92
+ return directiveRegistry.get(name)?.fn;
93
+ }
94
+ /**
95
+ * Get all registered directive names.
96
+ *
97
+ * @returns Array of directive names
98
+ */
99
+ export function getDirectiveNames() {
100
+ return Array.from(directiveRegistry.keys());
101
+ }
102
+ /**
103
+ * Clear all registered directives.
104
+ *
105
+ * @remarks
106
+ * Primarily useful for testing.
107
+ */
108
+ export function clearDirectives() {
109
+ directiveRegistry.clear();
110
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Vite plugin exports.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export { gonia, gonia as default } from './plugin.js';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Vite plugin exports.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export { gonia, gonia as default } from './plugin.js';