@heshammesalam/heshjs 0.0.0-stage → 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.
@@ -0,0 +1,49 @@
1
+ import { mount, element } from '../dom/index.js';
2
+ import { signal } from '../reactivity/index.js';
3
+ let current;
4
+ export function useModule() {
5
+ if (!current)
6
+ throw new Error('Module context is only available during a module view mount');
7
+ return current.context;
8
+ }
9
+ export function useController() { return useModule().controller; }
10
+ export function useModel() { return useModule().model; }
11
+ /** Child route outlet. Import this component in a .hesh layout and call Outlet(). */
12
+ export function Outlet(parent) {
13
+ if (!current)
14
+ throw new Error('Outlet requires a module view');
15
+ if (current.outlet)
16
+ throw new Error('A module may render only one Outlet');
17
+ current.outlet = element(parent, 'div');
18
+ current.outlet.setAttribute('data-hesh-outlet', '');
19
+ }
20
+ export function defineModule(definition) {
21
+ return {
22
+ attach(parent, location, parentContext, options) {
23
+ const route = signal(location);
24
+ let state;
25
+ const dispose = mount(() => {
26
+ const context = { route: { get: route.get }, parent: parentContext };
27
+ const model = definition.model(context);
28
+ const controller = definition.controller(model, context);
29
+ for (const value of [model, controller]) {
30
+ if (value && typeof value.then === 'function')
31
+ throw new Error('HMVC model/controller factories must be synchronous');
32
+ }
33
+ state = { context: { ...context, model, controller } };
34
+ const previous = current;
35
+ current = state;
36
+ try {
37
+ const result = definition.view(parent);
38
+ if (result && typeof result.then === 'function')
39
+ throw new Error('HMVC views must mount synchronously');
40
+ }
41
+ finally {
42
+ current = previous;
43
+ }
44
+ }, parent, options);
45
+ return { context: state.context, outlet: state.outlet, dispose, update: next => { route.set(next); } };
46
+ }
47
+ };
48
+ }
49
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hmvc/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,OAAO,EAAqC,MAAM,iBAAiB,CAAC;AACpF,OAAO,EAAE,MAAM,EAA8B,MAAM,wBAAwB,CAAC;AA6B5E,IAAI,OAA8D,CAAC;AAEnE,MAAM,UAAU,SAAS;IACvB,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IAC7F,OAAO,OAAO,CAAC,OAA8B,CAAC;AAChD,CAAC;AACD,MAAM,UAAU,aAAa,KAAqB,OAAO,SAAS,EAAc,CAAC,UAAU,CAAC,CAAC,CAAC;AAC9F,MAAM,UAAU,QAAQ,KAAqB,OAAO,SAAS,EAAK,CAAC,KAAK,CAAC,CAAC,CAAC;AAE3E,qFAAqF;AACrF,MAAM,UAAU,MAAM,CAAC,MAAY;IACjC,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IAC/D,IAAI,OAAO,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAC3E,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACvC,OAAO,CAAC,MAAsB,CAAC,YAAY,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,YAAY,CAAO,UAAkC;IACnE,OAAO;QACL,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO;YAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC/B,IAAI,KAA4D,CAAC;YACjE,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,EAAE;gBACzB,MAAM,OAAO,GAAG,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;gBACrE,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBACxC,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;gBACzD,KAAK,MAAM,KAAK,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,EAAE,CAAC;oBACxC,IAAI,KAAK,IAAI,OAAQ,KAA4B,CAAC,IAAI,KAAK,UAAU;wBAAE,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;gBAChJ,CAAC;gBACD,KAAK,GAAG,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,CAAC;gBACvD,MAAM,QAAQ,GAAG,OAAO,CAAC;gBACzB,OAAO,GAAG,KAAK,CAAC;gBAChB,IAAI,CAAC;oBACH,MAAM,MAAM,GAAY,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;oBAChD,IAAI,MAAM,IAAI,OAAQ,MAA6B,CAAC,IAAI,KAAK,UAAU;wBAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;gBAClI,CAAC;wBAAS,CAAC;oBAAC,OAAO,GAAG,QAAQ,CAAC;gBAAC,CAAC;YACnC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YACpB,OAAO,EAAE,OAAO,EAAE,KAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3G,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { mount, element, type Component, type MountOptions } from '../dom/index.js';\nimport { signal, type Signal, type Disposer } from '../reactivity/index.js';\n\nexport interface RouteLocation {\n readonly url: URL;\n readonly pathname: string;\n readonly params: Readonly<Record<string, string>>;\n readonly query: URLSearchParams;\n readonly hash: string;\n}\nexport interface ModuleContext<M = unknown, C = unknown> {\n readonly route: Pick<Signal<RouteLocation>, 'get'>;\n readonly parent?: ModuleContext;\n readonly model: M;\n readonly controller: C;\n}\nexport interface ModuleDefinition<M, C> {\n model: (context: Pick<ModuleContext, 'route' | 'parent'>) => M;\n controller: (model: M, context: Pick<ModuleContext, 'route' | 'parent'>) => C;\n view: Component;\n}\nexport interface MountedModule {\n readonly context: ModuleContext;\n readonly outlet?: Node;\n readonly dispose: Disposer;\n update(location: RouteLocation): void;\n}\nexport interface AppModule {\n attach(parent: Node, location: RouteLocation, parentContext?: ModuleContext, options?: MountOptions): MountedModule;\n}\nlet current: { context: ModuleContext; outlet?: Node } | undefined;\n\nexport function useModule<M = unknown, C = unknown>(): ModuleContext<M, C> {\n if (!current) throw new Error('Module context is only available during a module view mount');\n return current.context as ModuleContext<M, C>;\n}\nexport function useController<C = unknown>(): C { return useModule<unknown, C>().controller; }\nexport function useModel<M = unknown>(): M { return useModule<M>().model; }\n\n/** Child route outlet. Import this component in a .hesh layout and call Outlet(). */\nexport function Outlet(parent: Node): void {\n if (!current) throw new Error('Outlet requires a module view');\n if (current.outlet) throw new Error('A module may render only one Outlet');\n current.outlet = element(parent, 'div');\n (current.outlet as HTMLElement).setAttribute('data-hesh-outlet', '');\n}\n\nexport function defineModule<M, C>(definition: ModuleDefinition<M, C>): AppModule {\n return {\n attach(parent, location, parentContext, options) {\n const route = signal(location);\n let state: { context: ModuleContext; outlet?: Node } | undefined;\n const dispose = mount(() => {\n const context = { route: { get: route.get }, parent: parentContext };\n const model = definition.model(context);\n const controller = definition.controller(model, context);\n for (const value of [model, controller]) {\n if (value && typeof (value as { then?: unknown }).then === 'function') throw new Error('HMVC model/controller factories must be synchronous');\n }\n state = { context: { ...context, model, controller } };\n const previous = current;\n current = state;\n try {\n const result: unknown = definition.view(parent);\n if (result && typeof (result as { then?: unknown }).then === 'function') throw new Error('HMVC views must mount synchronously');\n } finally { current = previous; }\n }, parent, options);\n return { context: state!.context, outlet: state!.outlet, dispose, update: next => { route.set(next); } };\n }\n };\n}\n"]}
@@ -0,0 +1,47 @@
1
+ import ts from 'typescript';
2
+ export interface Location {
3
+ start: number;
4
+ end: number;
5
+ }
6
+ export interface Expression extends Location {
7
+ source: string;
8
+ }
9
+ export interface State extends Location {
10
+ kind: 'state' | 'const';
11
+ name: string;
12
+ value: Expression;
13
+ }
14
+ export interface Element extends Location {
15
+ kind: 'element';
16
+ name: string;
17
+ value?: Expression;
18
+ handler?: Expression;
19
+ async: boolean;
20
+ children: Element[];
21
+ }
22
+ export interface View extends Location {
23
+ name: string;
24
+ exported: boolean;
25
+ default: boolean;
26
+ body: (State | Element)[];
27
+ }
28
+ export interface Module {
29
+ source: string;
30
+ filename: string;
31
+ imports: Expression[];
32
+ views: View[];
33
+ }
34
+ export declare class HeshError extends Error {
35
+ readonly source: string;
36
+ readonly offset: number;
37
+ readonly filename: string;
38
+ readonly line: number;
39
+ readonly column: number;
40
+ constructor(message: string, source: string, offset: number, filename?: string);
41
+ }
42
+ export interface Token extends Location {
43
+ text: string;
44
+ kind: ts.SyntaxKind;
45
+ }
46
+ export declare function tokenize(source: string, filename?: string): Token[];
47
+ export declare function parse(source: string, filename?: string): Module;
@@ -0,0 +1,218 @@
1
+ import ts from 'typescript';
2
+ export class HeshError extends Error {
3
+ source;
4
+ offset;
5
+ filename;
6
+ line;
7
+ column;
8
+ constructor(message, source, offset, filename = 'component.hesh') {
9
+ const lines = source.slice(0, offset).split('\n');
10
+ const line = lines.length, column = lines.at(-1).length + 1;
11
+ super(`${filename}:${line}:${column}: ${message}`);
12
+ this.source = source;
13
+ this.offset = offset;
14
+ this.filename = filename;
15
+ this.name = 'HeshError';
16
+ this.line = line;
17
+ this.column = column;
18
+ }
19
+ }
20
+ export function tokenize(source, filename = 'component.hesh') {
21
+ const tokens = [];
22
+ const keywords = new Set('break case catch class const continue debugger default delete do else enum export extends false finally for function if import in instanceof new null return super switch this throw true try typeof var void while with yield let static await async'.split(' '));
23
+ let at = 0;
24
+ const fail = (message, offset) => { throw new HeshError(message, source, offset, filename); };
25
+ while (at < source.length) {
26
+ const start = at;
27
+ const ch = source[at];
28
+ if (/\s/.test(ch)) {
29
+ at++;
30
+ continue;
31
+ }
32
+ if (source.startsWith('//', at)) {
33
+ while (at < source.length && source[at] !== '\n')
34
+ at++;
35
+ continue;
36
+ }
37
+ if (source.startsWith('/*', at)) {
38
+ const end = source.indexOf('*/', at + 2);
39
+ if (end < 0)
40
+ fail('Unterminated comment', at);
41
+ at = end + 2;
42
+ continue;
43
+ }
44
+ let kind = ts.SyntaxKind.Unknown;
45
+ if (ch === '"' || ch === "'" || ch === '`') {
46
+ const quote = ch;
47
+ at++;
48
+ let closed = false;
49
+ while (at < source.length) {
50
+ const current = source[at++];
51
+ if (current === '\\') {
52
+ at++;
53
+ continue;
54
+ }
55
+ if (quote === '`' && current === '$' && source[at] === '{')
56
+ fail('Template substitutions belong in imported JS/TS helpers in Milestone 1', at - 1);
57
+ if (current === quote) {
58
+ closed = true;
59
+ break;
60
+ }
61
+ if (quote !== '`' && (current === '\n' || current === '\r'))
62
+ fail('Unterminated string', start);
63
+ }
64
+ if (!closed)
65
+ fail('Unterminated string', start);
66
+ kind = quote === '`' ? ts.SyntaxKind.NoSubstitutionTemplateLiteral : ts.SyntaxKind.StringLiteral;
67
+ }
68
+ else if (/[$_\p{ID_Start}]/u.test(ch)) {
69
+ at++;
70
+ while (at < source.length && /[$\u200c\u200d\p{ID_Continue}]/u.test(source[at]))
71
+ at++;
72
+ kind = keywords.has(source.slice(start, at)) ? ts.SyntaxKind.Unknown : ts.SyntaxKind.Identifier;
73
+ }
74
+ else if (/\d/.test(ch)) {
75
+ at++;
76
+ while (at < source.length && /[\w.]/.test(source[at]))
77
+ at++;
78
+ kind = ts.SyntaxKind.NumericLiteral;
79
+ }
80
+ else {
81
+ const operator = ['>>>=', '===', '!==', '**=', '&&=', '||=', '??=', '>>>', '<<=', '>>=', '=>', '==', '!=', '<=', '>=', '++', '--', '+=', '-=', '*=', '/=', '%=', '**', '&&', '||', '??', '?.', '<<', '>>', '&=', '|=', '^=', '...'].find(op => source.startsWith(op, at));
82
+ at += operator?.length ?? 1;
83
+ }
84
+ tokens.push({ kind, text: source.slice(start, at), start, end: at });
85
+ }
86
+ return tokens;
87
+ }
88
+ export function parse(source, filename = 'component.hesh') {
89
+ const tokens = tokenize(source, filename);
90
+ let at = 0;
91
+ const peek = () => tokens[at]?.text;
92
+ const fail = (message, offset = tokens[at]?.start ?? source.length) => { throw new HeshError(message, source, offset, filename); };
93
+ const take = (text) => {
94
+ const token = tokens[at];
95
+ if (!token || (text !== undefined && token.text !== text))
96
+ fail(`Expected ${text ?? 'token'}, received ${peek() ?? 'end of file'}`);
97
+ at++;
98
+ return token;
99
+ };
100
+ const identifier = () => {
101
+ const token = take();
102
+ if (token.kind !== ts.SyntaxKind.Identifier)
103
+ fail('Expected identifier', token.start);
104
+ if (token.text.startsWith('__h'))
105
+ fail('Identifiers beginning __h are reserved', token.start);
106
+ return token;
107
+ };
108
+ const expression = (ends) => {
109
+ const start = tokens[at]?.start ?? source.length;
110
+ const stack = [];
111
+ const pairs = { '(': ')', '[': ']', '{': '}' };
112
+ while (tokens[at]) {
113
+ const token = tokens[at];
114
+ if (!stack.length && ends.includes(token.text))
115
+ break;
116
+ if (pairs[token.text])
117
+ stack.push(pairs[token.text]);
118
+ else if ([')', ']', '}'].includes(token.text)) {
119
+ if (stack.pop() !== token.text)
120
+ fail('Unbalanced expression', token.start);
121
+ }
122
+ at++;
123
+ }
124
+ if (stack.length)
125
+ fail('Unterminated expression', start);
126
+ const end = tokens[at]?.start ?? source.length;
127
+ if (!source.slice(start, end).trim())
128
+ fail('Expected expression', start);
129
+ return { source: source.slice(start, end).trimEnd(), start, end };
130
+ };
131
+ const element = () => {
132
+ const name = identifier();
133
+ take('(');
134
+ let value, handler, async = false;
135
+ if (peek() !== ')')
136
+ value = expression([',', ')']);
137
+ if (peek() === ',') {
138
+ take(',');
139
+ if (peek() === 'async') {
140
+ take();
141
+ async = true;
142
+ }
143
+ const start = take('{').end;
144
+ let depth = 1;
145
+ while (tokens[at] && depth) {
146
+ if (peek() === '{')
147
+ depth++;
148
+ if (peek() === '}')
149
+ depth--;
150
+ if (depth)
151
+ at++;
152
+ }
153
+ if (!tokens[at])
154
+ fail('Unterminated event block', start);
155
+ const end = take('}').start;
156
+ handler = { source: source.slice(start, end), start, end };
157
+ }
158
+ take(')');
159
+ const children = [];
160
+ if (peek() === '{') {
161
+ take();
162
+ while (peek() && peek() !== '}')
163
+ children.push(element());
164
+ take('}');
165
+ if (peek() === ';')
166
+ take();
167
+ }
168
+ else
169
+ take(';');
170
+ return { kind: 'element', name: name.text, start: name.start, end: tokens[at - 1].end, value, handler, async, children };
171
+ };
172
+ const module = { source, filename, imports: [], views: [] };
173
+ while (tokens[at]) {
174
+ if (peek() === 'import') {
175
+ if (module.views.length)
176
+ fail('Imports must precede views');
177
+ const start = take().start;
178
+ while (peek() && peek() !== ';')
179
+ take();
180
+ const end = take(';').end;
181
+ module.imports.push({ source: source.slice(start, end), start, end });
182
+ continue;
183
+ }
184
+ let exported = false, isDefault = false;
185
+ if (peek() === 'export') {
186
+ take();
187
+ exported = true;
188
+ }
189
+ if (peek() === 'default') {
190
+ take();
191
+ isDefault = true;
192
+ if (!exported)
193
+ fail('default requires export');
194
+ }
195
+ const start = take('view').start;
196
+ const name = identifier().text;
197
+ take('{');
198
+ const body = [];
199
+ while (peek() && peek() !== '}') {
200
+ if (peek() === 'const' || tokens[at + 1]?.text === '=') {
201
+ const kind = peek() === 'const' ? (take(), 'const') : 'state';
202
+ const name = identifier();
203
+ take('=');
204
+ const value = expression([';']);
205
+ const end = take(';').end;
206
+ body.push({ kind, name: name.text, value, start: name.start, end });
207
+ }
208
+ else
209
+ body.push(element());
210
+ }
211
+ const end = take('}').end;
212
+ module.views.push({ name, body, start, end, exported, default: isDefault });
213
+ }
214
+ if (!module.views.length)
215
+ fail('Expected at least one view');
216
+ return module;
217
+ }
218
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":"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","sourcesContent":["import ts from 'typescript';\nexport interface Location { start: number; end: number }\nexport interface Expression extends Location { source: string }\nexport interface State extends Location { kind: 'state' | 'const'; name: string; value: Expression }\nexport interface Element extends Location { kind: 'element'; name: string; value?: Expression; handler?: Expression; async: boolean; children: Element[] }\nexport interface View extends Location { name: string; exported: boolean; default: boolean; body: (State | Element)[] }\nexport interface Module { source: string; filename: string; imports: Expression[]; views: View[] }\nexport class HeshError extends Error {\n readonly line: number;\n readonly column: number;\n constructor(message: string, readonly source: string, readonly offset: number, readonly filename = 'component.hesh') {\n const lines = source.slice(0, offset).split('\\n');\n const line = lines.length, column = lines.at(-1)!.length + 1;\n super(`${filename}:${line}:${column}: ${message}`);\n this.name = 'HeshError'; this.line = line; this.column = column;\n }\n}\nexport interface Token extends Location { text: string; kind: ts.SyntaxKind }\nexport function tokenize(source: string, filename = 'component.hesh'): Token[] {\n const tokens: Token[] = [];\n const keywords = new Set('break case catch class const continue debugger default delete do else enum export extends false finally for function if import in instanceof new null return super switch this throw true try typeof var void while with yield let static await async'.split(' '));\n let at = 0;\n const fail = (message: string, offset: number): never => { throw new HeshError(message, source, offset, filename); };\n while (at < source.length) {\n const start = at;\n const ch = source[at]!;\n if (/\\s/.test(ch)) { at++; continue; }\n if (source.startsWith('//', at)) { while (at < source.length && source[at] !== '\\n') at++; continue; }\n if (source.startsWith('/*', at)) {\n const end = source.indexOf('*/', at + 2);\n if (end < 0) fail('Unterminated comment', at);\n at = end + 2; continue;\n }\n let kind = ts.SyntaxKind.Unknown;\n if (ch === '\"' || ch === \"'\" || ch === '`') {\n const quote = ch;\n at++; let closed = false;\n while (at < source.length) {\n const current = source[at++]!;\n if (current === '\\\\') { at++; continue; }\n if (quote === '`' && current === '$' && source[at] === '{') fail('Template substitutions belong in imported JS/TS helpers in Milestone 1', at - 1);\n if (current === quote) { closed = true; break; }\n if (quote !== '`' && (current === '\\n' || current === '\\r')) fail('Unterminated string', start);\n }\n if (!closed) fail('Unterminated string', start);\n kind = quote === '`' ? ts.SyntaxKind.NoSubstitutionTemplateLiteral : ts.SyntaxKind.StringLiteral;\n } else if (/[$_\\p{ID_Start}]/u.test(ch)) {\n at++;\n while (at < source.length && /[$\\u200c\\u200d\\p{ID_Continue}]/u.test(source[at]!)) at++;\n kind = keywords.has(source.slice(start, at)) ? ts.SyntaxKind.Unknown : ts.SyntaxKind.Identifier;\n } else if (/\\d/.test(ch)) {\n at++;\n while (at < source.length && /[\\w.]/.test(source[at]!)) at++;\n kind = ts.SyntaxKind.NumericLiteral;\n } else {\n const operator = ['>>>=', '===', '!==', '**=', '&&=', '||=', '??=', '>>>', '<<=', '>>=', '=>', '==', '!=', '<=', '>=', '++', '--', '+=', '-=', '*=', '/=', '%=', '**', '&&', '||', '??', '?.', '<<', '>>', '&=', '|=', '^=', '...'].find(op => source.startsWith(op, at));\n at += operator?.length ?? 1;\n }\n tokens.push({ kind, text: source.slice(start, at), start, end: at });\n }\n return tokens;\n}\n\nexport function parse(source: string, filename = 'component.hesh'): Module {\n const tokens = tokenize(source, filename);\n let at = 0;\n const peek = () => tokens[at]?.text;\n const fail = (message: string, offset = tokens[at]?.start ?? source.length): never => { throw new HeshError(message, source, offset, filename); };\n const take = (text?: string): Token => {\n const token = tokens[at];\n if (!token || (text !== undefined && token.text !== text)) fail(`Expected ${text ?? 'token'}, received ${peek() ?? 'end of file'}`);\n at++; return token!;\n };\n const identifier = () => {\n const token = take();\n if (token.kind !== ts.SyntaxKind.Identifier) fail('Expected identifier', token.start);\n if (token.text.startsWith('__h')) fail('Identifiers beginning __h are reserved', token.start);\n return token;\n };\n const expression = (ends: string[]): Expression => {\n const start = tokens[at]?.start ?? source.length;\n const stack: string[] = [];\n const pairs: Record<string, string> = { '(': ')', '[': ']', '{': '}' };\n while (tokens[at]) {\n const token = tokens[at]!;\n if (!stack.length && ends.includes(token.text)) break;\n if (pairs[token.text]) stack.push(pairs[token.text]!);\n else if ([')', ']', '}'].includes(token.text)) {\n if (stack.pop() !== token.text) fail('Unbalanced expression', token.start);\n }\n at++;\n }\n if (stack.length) fail('Unterminated expression', start);\n const end = tokens[at]?.start ?? source.length;\n if (!source.slice(start, end).trim()) fail('Expected expression', start);\n return { source: source.slice(start, end).trimEnd(), start, end };\n };\n const element = (): Element => {\n const name = identifier(); take('(');\n let value: Expression | undefined, handler: Expression | undefined, async = false;\n if (peek() !== ')') value = expression([',', ')']);\n if (peek() === ',') {\n take(',');\n if (peek() === 'async') { take(); async = true; }\n const start = take('{').end;\n let depth = 1;\n while (tokens[at] && depth) {\n if (peek() === '{') depth++;\n if (peek() === '}') depth--;\n if (depth) at++;\n }\n if (!tokens[at]) fail('Unterminated event block', start);\n const end = take('}').start;\n handler = { source: source.slice(start, end), start, end };\n }\n take(')');\n const children: Element[] = [];\n if (peek() === '{') { take(); while (peek() && peek() !== '}') children.push(element()); take('}'); if (peek() === ';') take(); }\n else take(';');\n return { kind: 'element', name: name.text, start: name.start, end: tokens[at - 1]!.end, value, handler, async, children };\n };\n const module: Module = { source, filename, imports: [], views: [] };\n while (tokens[at]) {\n if (peek() === 'import') {\n if (module.views.length) fail('Imports must precede views');\n const start = take().start;\n while (peek() && peek() !== ';') take();\n const end = take(';').end;\n module.imports.push({ source: source.slice(start, end), start, end }); continue;\n }\n let exported = false, isDefault = false;\n if (peek() === 'export') { take(); exported = true; }\n if (peek() === 'default') { take(); isDefault = true; if (!exported) fail('default requires export'); }\n const start = take('view').start;\n const name = identifier().text; take('{');\n const body: (State | Element)[] = [];\n while (peek() && peek() !== '}') {\n if (peek() === 'const' || tokens[at + 1]?.text === '=') {\n const kind = peek() === 'const' ? (take(), 'const' as const) : 'state' as const;\n const name = identifier(); take('='); const value = expression([';']); const end = take(';').end;\n body.push({ kind, name: name.text, value, start: name.start, end });\n } else body.push(element());\n }\n const end = take('}').end;\n module.views.push({ name, body, start, end, exported, default: isDefault });\n }\n if (!module.views.length) fail('Expected at least one view');\n return module;\n}\n"]}
@@ -0,0 +1,15 @@
1
+ export type Disposer = () => void;
2
+ export interface Signal<T> {
3
+ get(): T;
4
+ set(value: T): T;
5
+ }
6
+ export declare function signal<T>(initial: T): Signal<T>;
7
+ export declare function onCleanup(fn: Disposer): void;
8
+ export declare function scope<T>(fn: () => T): {
9
+ value: T;
10
+ dispose: Disposer;
11
+ };
12
+ export declare function effect(fn: () => void, computed?: boolean): Disposer;
13
+ export declare function derived<T>(fn: () => T): Pick<Signal<T>, 'get'>;
14
+ export declare function batch<T>(fn: () => T): T;
15
+ export declare function untrack<T>(fn: () => T): T;
@@ -0,0 +1,162 @@
1
+ let active;
2
+ let owner;
3
+ let depth = 0;
4
+ let flushing = false;
5
+ const queue = new Set();
6
+ function flush() {
7
+ if (depth || flushing)
8
+ return;
9
+ flushing = true;
10
+ let steps = 0;
11
+ try {
12
+ while (queue.size) {
13
+ if (++steps > 1000)
14
+ throw new Error('HeshJS reactive update limit exceeded');
15
+ const next = [...queue].find(o => o.computed) ?? queue.values().next().value;
16
+ queue.delete(next);
17
+ if (!next.disposed)
18
+ next.run();
19
+ }
20
+ }
21
+ catch (error) {
22
+ queue.clear();
23
+ throw error;
24
+ }
25
+ finally {
26
+ flushing = false;
27
+ }
28
+ }
29
+ export function signal(initial) {
30
+ let value = initial;
31
+ const subscribers = new Set();
32
+ return {
33
+ get() { if (active) {
34
+ subscribers.add(active);
35
+ active.deps.add(subscribers);
36
+ } return value; },
37
+ set(next) {
38
+ if (!Object.is(value, next)) {
39
+ value = next;
40
+ for (const observer of subscribers)
41
+ queue.add(observer);
42
+ flush();
43
+ }
44
+ return value;
45
+ }
46
+ };
47
+ }
48
+ export function onCleanup(fn) {
49
+ if (!owner)
50
+ throw new Error('onCleanup requires a reactive scope');
51
+ if (owner.disposed)
52
+ fn();
53
+ else
54
+ owner.cleanups.add(fn);
55
+ }
56
+ export function scope(fn) {
57
+ const parent = owner;
58
+ const current = { cleanups: new Set(), disposed: false };
59
+ const dispose = () => {
60
+ if (current.disposed)
61
+ return;
62
+ current.disposed = true;
63
+ parent?.cleanups.delete(dispose);
64
+ const errors = [];
65
+ for (const cleanup of [...current.cleanups].reverse()) {
66
+ try {
67
+ cleanup();
68
+ }
69
+ catch (error) {
70
+ errors.push(error);
71
+ }
72
+ }
73
+ current.cleanups.clear();
74
+ if (errors.length)
75
+ throw new AggregateError(errors, 'HeshJS cleanup failed');
76
+ };
77
+ parent?.cleanups.add(dispose);
78
+ owner = current;
79
+ try {
80
+ return { value: fn(), dispose };
81
+ }
82
+ catch (error) {
83
+ dispose();
84
+ throw error;
85
+ }
86
+ finally {
87
+ owner = parent;
88
+ }
89
+ }
90
+ export function effect(fn, computed = false) {
91
+ const owningScope = owner;
92
+ let runScope;
93
+ const observer = {
94
+ deps: new Set(), disposed: false, computed,
95
+ run() {
96
+ for (const dep of observer.deps)
97
+ dep.delete(observer);
98
+ observer.deps.clear();
99
+ runScope?.dispose();
100
+ const previous = active;
101
+ const previousOwner = owner;
102
+ active = observer;
103
+ owner = owningScope;
104
+ depth++;
105
+ try {
106
+ runScope = scope(fn);
107
+ }
108
+ finally {
109
+ active = previous;
110
+ owner = previousOwner;
111
+ depth--;
112
+ }
113
+ }
114
+ };
115
+ const dispose = () => {
116
+ if (observer.disposed)
117
+ return;
118
+ observer.disposed = true;
119
+ owningScope?.cleanups.delete(dispose);
120
+ queue.delete(observer);
121
+ for (const dep of observer.deps)
122
+ dep.delete(observer);
123
+ observer.deps.clear();
124
+ runScope?.dispose();
125
+ };
126
+ owningScope?.cleanups.add(dispose);
127
+ try {
128
+ observer.run();
129
+ flush();
130
+ }
131
+ catch (error) {
132
+ dispose();
133
+ throw error;
134
+ }
135
+ return dispose;
136
+ }
137
+ export function derived(fn) {
138
+ const value = signal(undefined);
139
+ effect(() => value.set(fn()), true);
140
+ return { get: value.get };
141
+ }
142
+ export function batch(fn) {
143
+ depth++;
144
+ try {
145
+ return fn();
146
+ }
147
+ finally {
148
+ depth--;
149
+ flush();
150
+ }
151
+ }
152
+ export function untrack(fn) {
153
+ const previous = active;
154
+ active = undefined;
155
+ try {
156
+ return fn();
157
+ }
158
+ finally {
159
+ active = previous;
160
+ }
161
+ }
162
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/reactivity/index.ts"],"names":[],"mappings":"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","sourcesContent":["export type Disposer = () => void;\nexport interface Signal<T> { get(): T; set(value: T): T }\ninterface Observer { run(): void; deps: Set<Set<Observer>>; disposed: boolean; computed: boolean }\ninterface Owner { cleanups: Set<Disposer>; disposed: boolean }\nlet active: Observer | undefined;\nlet owner: Owner | undefined;\nlet depth = 0;\nlet flushing = false;\nconst queue = new Set<Observer>();\n\nfunction flush(): void {\n if (depth || flushing) return;\n flushing = true;\n let steps = 0;\n try {\n while (queue.size) {\n if (++steps > 1000) throw new Error('HeshJS reactive update limit exceeded');\n const next = [...queue].find(o => o.computed) ?? queue.values().next().value!;\n queue.delete(next);\n if (!next.disposed) next.run();\n }\n } catch (error) { queue.clear(); throw error; }\n finally { flushing = false; }\n}\n\nexport function signal<T>(initial: T): Signal<T> {\n let value = initial;\n const subscribers = new Set<Observer>();\n return {\n get() { if (active) { subscribers.add(active); active.deps.add(subscribers); } return value; },\n set(next) {\n if (!Object.is(value, next)) {\n value = next;\n for (const observer of subscribers) queue.add(observer);\n flush();\n }\n return value;\n }\n };\n}\n\nexport function onCleanup(fn: Disposer): void {\n if (!owner) throw new Error('onCleanup requires a reactive scope');\n if (owner.disposed) fn(); else owner.cleanups.add(fn);\n}\n\nexport function scope<T>(fn: () => T): { value: T; dispose: Disposer } {\n const parent = owner;\n const current: Owner = { cleanups: new Set(), disposed: false };\n const dispose = () => {\n if (current.disposed) return;\n current.disposed = true;\n parent?.cleanups.delete(dispose);\n const errors: unknown[] = [];\n for (const cleanup of [...current.cleanups].reverse()) {\n try { cleanup(); } catch (error) { errors.push(error); }\n }\n current.cleanups.clear();\n if (errors.length) throw new AggregateError(errors, 'HeshJS cleanup failed');\n };\n parent?.cleanups.add(dispose);\n owner = current;\n try { return { value: fn(), dispose }; }\n catch (error) { dispose(); throw error; }\n finally { owner = parent; }\n}\n\nexport function effect(fn: () => void, computed = false): Disposer {\n const owningScope = owner;\n let runScope: { dispose: Disposer } | undefined;\n const observer: Observer = {\n deps: new Set(), disposed: false, computed,\n run() {\n for (const dep of observer.deps) dep.delete(observer);\n observer.deps.clear();\n runScope?.dispose();\n const previous = active;\n const previousOwner = owner;\n active = observer;\n owner = owningScope;\n depth++;\n try { runScope = scope(fn); }\n finally { active = previous; owner = previousOwner; depth--; }\n }\n };\n const dispose = () => {\n if (observer.disposed) return;\n observer.disposed = true;\n owningScope?.cleanups.delete(dispose);\n queue.delete(observer);\n for (const dep of observer.deps) dep.delete(observer);\n observer.deps.clear();\n runScope?.dispose();\n };\n owningScope?.cleanups.add(dispose);\n try { observer.run(); flush(); } catch (error) { dispose(); throw error; }\n return dispose;\n}\n\nexport function derived<T>(fn: () => T): Pick<Signal<T>, 'get'> {\n const value = signal<T>(undefined as T);\n effect(() => value.set(fn()), true);\n return { get: value.get };\n}\n\nexport function batch<T>(fn: () => T): T {\n depth++;\n try { return fn(); } finally { depth--; flush(); }\n}\n\nexport function untrack<T>(fn: () => T): T {\n const previous = active;\n active = undefined;\n try { return fn(); } finally { active = previous; }\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import { type Disposer } from '../reactivity/index.js';
2
+ import type { AppModule, RouteLocation } from '../hmvc/index.js';
3
+ export type { RouteLocation } from '../hmvc/index.js';
4
+ export interface RouteRecord {
5
+ path: string;
6
+ module: AppModule;
7
+ children?: readonly RouteRecord[];
8
+ }
9
+ export interface RouterOptions {
10
+ routes: readonly RouteRecord[];
11
+ window?: Window;
12
+ notFound?: AppModule;
13
+ onError?: (error: unknown) => void;
14
+ }
15
+ export declare function createRouter(options: RouterOptions): {
16
+ location: {
17
+ get: () => RouteLocation;
18
+ };
19
+ navigate: (to: string, navigation?: {
20
+ replace?: boolean;
21
+ }) => void;
22
+ stop: () => void;
23
+ start(parent: Node): Disposer;
24
+ };