@heshammesalam/heshjs 0.2.0 → 0.3.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 +8 -2
- package/cli.js +26 -0
- package/dist/architecture/diagnostics.d.ts +12 -0
- package/dist/architecture/diagnostics.js +8 -0
- package/dist/architecture/diagnostics.js.map +1 -0
- package/dist/architecture/html.d.ts +16 -0
- package/dist/architecture/html.js +132 -0
- package/dist/architecture/html.js.map +1 -0
- package/dist/architecture/index.d.ts +42 -0
- package/dist/architecture/index.js +418 -0
- package/dist/architecture/index.js.map +1 -0
- package/dist/compiler/index.js +2 -0
- package/dist/compiler/index.js.map +1 -1
- package/dist/destinations/index.d.ts +63 -0
- package/dist/destinations/index.js +185 -0
- package/dist/destinations/index.js.map +1 -0
- package/dist/dom/index.d.ts +2 -0
- package/dist/dom/index.js +5 -1
- package/dist/dom/index.js.map +1 -1
- package/dist/domains/index.d.ts +24 -0
- package/dist/domains/index.js +110 -0
- package/dist/domains/index.js.map +1 -0
- package/dist/parser/index.d.ts +15 -1
- package/dist/parser/index.js +73 -3
- package/dist/parser/index.js.map +1 -1
- package/dist/presentation/index.d.ts +9 -0
- package/dist/presentation/index.js +38 -0
- package/dist/presentation/index.js.map +1 -0
- package/dist/vite/index.d.ts +4 -1
- package/dist/vite/index.js +36 -2
- package/dist/vite/index.js.map +1 -1
- package/docs/contracts.md +60 -0
- package/docs/domain-graph.md +205 -0
- package/docs/grammar.md +4 -0
- package/package.json +8 -3
- package/src/architecture/diagnostics.ts +11 -0
- package/src/architecture/html.ts +88 -0
- package/src/architecture/index.ts +283 -0
- package/src/compiler/index.ts +1 -0
- package/src/destinations/index.ts +146 -0
- package/src/dom/index.ts +6 -1
- package/src/domains/index.ts +97 -0
- package/src/parser/index.ts +55 -4
- package/src/presentation/index.ts +31 -0
- package/src/vite/index.ts +24 -2
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { scope } from '../reactivity/index.js';
|
|
2
|
+
export class ClientPolicyDenied extends Error {
|
|
3
|
+
policy;
|
|
4
|
+
contract;
|
|
5
|
+
code = 'HESH-ACCESS-001';
|
|
6
|
+
constructor(policy, contract) {
|
|
7
|
+
super(`Client policy denied: ${policy} (${contract}). Server authorization is still required.`);
|
|
8
|
+
this.policy = policy;
|
|
9
|
+
this.contract = contract;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
const unsafe = new Set(['__proto__', 'constructor', 'prototype']);
|
|
13
|
+
export function contractPath(path) {
|
|
14
|
+
const parts = path.split('.');
|
|
15
|
+
if (parts.some(p => !p || unsafe.has(p)))
|
|
16
|
+
throw new Error(`Invalid contract path ${path}`);
|
|
17
|
+
return parts;
|
|
18
|
+
}
|
|
19
|
+
function project(target, path, value) {
|
|
20
|
+
const name = path[0];
|
|
21
|
+
if (path.length === 1) {
|
|
22
|
+
target[name] = value;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (target[name] !== undefined && (typeof target[name] !== 'object' || target[name] === null))
|
|
26
|
+
throw new Error(`Overlapping public contracts at ${name}`);
|
|
27
|
+
const next = (target[name] ??= Object.create(null));
|
|
28
|
+
project(next, path.slice(1), value);
|
|
29
|
+
}
|
|
30
|
+
function freeze(value) {
|
|
31
|
+
for (const child of Object.values(value))
|
|
32
|
+
if (child && typeof child === 'object')
|
|
33
|
+
freeze(child);
|
|
34
|
+
return Object.freeze(value);
|
|
35
|
+
}
|
|
36
|
+
function lookup(value, parts) {
|
|
37
|
+
let receiver;
|
|
38
|
+
for (const part of parts) {
|
|
39
|
+
receiver = value;
|
|
40
|
+
value = value != null && Object.hasOwn(Object(value), part) ? value[part] : undefined;
|
|
41
|
+
}
|
|
42
|
+
return { value, receiver };
|
|
43
|
+
}
|
|
44
|
+
export function createDomainGraph(definitions, options = {}) {
|
|
45
|
+
const byName = new Map(definitions.map(d => [d.name, d]));
|
|
46
|
+
if (byName.size !== definitions.length)
|
|
47
|
+
throw new Error('Duplicate domain name');
|
|
48
|
+
const domains = Object.create(null);
|
|
49
|
+
const contexts = Object.create(null);
|
|
50
|
+
const building = new Set();
|
|
51
|
+
let disposed = false;
|
|
52
|
+
const owned = scope(() => {
|
|
53
|
+
const build = (name) => {
|
|
54
|
+
if (domains[name])
|
|
55
|
+
return domains[name];
|
|
56
|
+
const definition = byName.get(name);
|
|
57
|
+
if (!definition)
|
|
58
|
+
throw new Error(`Missing domain ${name}`);
|
|
59
|
+
if (building.has(name))
|
|
60
|
+
throw new Error(`Circular domain dependency at ${name}`);
|
|
61
|
+
building.add(name);
|
|
62
|
+
const dependencies = Object.create(null);
|
|
63
|
+
for (const dependency of definition.consumes) {
|
|
64
|
+
const [provider, ...operation] = contractPath(dependency);
|
|
65
|
+
if (!operation.length)
|
|
66
|
+
throw new Error(`Dependency must name an operation: ${dependency}`);
|
|
67
|
+
const target = build(provider);
|
|
68
|
+
const value = lookup(target, operation).value;
|
|
69
|
+
if (typeof value !== 'function')
|
|
70
|
+
throw new Error(`Missing public operation ${dependency}`);
|
|
71
|
+
project(dependencies[provider] ??= Object.create(null), operation, value);
|
|
72
|
+
}
|
|
73
|
+
freeze(dependencies);
|
|
74
|
+
const implementation = definition.factory(dependencies);
|
|
75
|
+
if (implementation && typeof implementation.then === 'function')
|
|
76
|
+
throw new Error(`Domain ${name} factory must be synchronous`);
|
|
77
|
+
const facade = Object.create(null);
|
|
78
|
+
for (const operation of definition.exposes) {
|
|
79
|
+
const parts = contractPath(operation);
|
|
80
|
+
const { value, receiver } = lookup(implementation, parts);
|
|
81
|
+
if (typeof value !== 'function')
|
|
82
|
+
throw new Error(`Unresolved implementation ${name}.${operation}`);
|
|
83
|
+
project(facade, parts, (...args) => {
|
|
84
|
+
if (disposed)
|
|
85
|
+
throw new Error('Domain graph is disposed');
|
|
86
|
+
const policy = definition.access?.[operation];
|
|
87
|
+
if (policy && options.authorize?.(policy) !== true)
|
|
88
|
+
throw new ClientPolicyDenied(policy, `${name}.${operation}`);
|
|
89
|
+
return Reflect.apply(value, receiver, args);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
domains[name] = freeze(facade);
|
|
93
|
+
contexts[name] = Object.freeze({ ...dependencies, [name]: domains[name] });
|
|
94
|
+
building.delete(name);
|
|
95
|
+
return domains[name];
|
|
96
|
+
};
|
|
97
|
+
for (const definition of definitions)
|
|
98
|
+
build(definition.name);
|
|
99
|
+
freeze(domains);
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
domains,
|
|
103
|
+
forDomain(name) { if (disposed)
|
|
104
|
+
throw new Error('Domain graph is disposed'); if (!contexts[name])
|
|
105
|
+
throw new Error(`Unknown domain context ${name}`); return contexts[name]; },
|
|
106
|
+
dispose() { if (disposed)
|
|
107
|
+
return; disposed = true; owned.dispose(); }
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/domains/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { scope, type Disposer } from '../reactivity/index.js';\nexport interface DomainDefinition {\n name: string;\n exposes: readonly string[];\n consumes: readonly string[];\n access?: Readonly<Record<string, string>>;\n factory: (dependencies: Record<string, Record<string, unknown>>) => unknown;\n}\nexport interface DomainOptions { authorize?: (policy: string) => boolean }\nexport class ClientPolicyDenied extends Error {\n readonly code = 'HESH-ACCESS-001';\n constructor(readonly policy: string, readonly contract: string) {\n super(`Client policy denied: ${policy} (${contract}). Server authorization is still required.`);\n }\n}\nexport interface DomainGraph<T = Record<string, Record<string, unknown>>> {\n readonly domains: T;\n forDomain(name: string): Record<string, unknown>;\n dispose: Disposer;\n}\nconst unsafe = new Set(['__proto__', 'constructor', 'prototype']);\nexport function contractPath(path: string): string[] {\n const parts = path.split('.');\n if (parts.some(p => !p || unsafe.has(p))) throw new Error(`Invalid contract path ${path}`);\n return parts;\n}\nfunction project(target: Record<string, unknown>, path: string[], value: unknown): void {\n const name = path[0]!;\n if (path.length === 1) { target[name] = value; return; }\n if (target[name] !== undefined && (typeof target[name] !== 'object' || target[name] === null)) throw new Error(`Overlapping public contracts at ${name}`);\n const next = (target[name] ??= Object.create(null)) as Record<string, unknown>;\n project(next, path.slice(1), value);\n}\nfunction freeze(value: Record<string, unknown>): Record<string, unknown> {\n for (const child of Object.values(value)) if (child && typeof child === 'object') freeze(child as Record<string, unknown>);\n return Object.freeze(value);\n}\nfunction lookup(value: unknown, parts: string[]): { value: unknown; receiver: unknown } {\n let receiver: unknown;\n for (const part of parts) {\n receiver = value;\n value = value != null && Object.hasOwn(Object(value), part) ? (value as Record<string, unknown>)[part] : undefined;\n }\n return { value, receiver };\n}\nexport function createDomainGraph(definitions: readonly DomainDefinition[], options: DomainOptions = {}): DomainGraph {\n const byName = new Map(definitions.map(d => [d.name, d]));\n if (byName.size !== definitions.length) throw new Error('Duplicate domain name');\n const domains: Record<string, Record<string, unknown>> = Object.create(null);\n const contexts: Record<string, Record<string, unknown>> = Object.create(null);\n const building = new Set<string>();\n let disposed = false;\n const owned = scope(() => {\n const build = (name: string): Record<string, unknown> => {\n if (domains[name]) return domains[name]!;\n const definition = byName.get(name);\n if (!definition) throw new Error(`Missing domain ${name}`);\n if (building.has(name)) throw new Error(`Circular domain dependency at ${name}`);\n building.add(name);\n const dependencies: Record<string, Record<string, unknown>> = Object.create(null);\n for (const dependency of definition.consumes) {\n const [provider, ...operation] = contractPath(dependency);\n if (!operation.length) throw new Error(`Dependency must name an operation: ${dependency}`);\n const target = build(provider!);\n const value = lookup(target, operation).value;\n if (typeof value !== 'function') throw new Error(`Missing public operation ${dependency}`);\n project(dependencies[provider!] ??= Object.create(null), operation, value);\n }\n freeze(dependencies);\n const implementation = definition.factory(dependencies);\n if (implementation && typeof (implementation as { then?: unknown }).then === 'function') throw new Error(`Domain ${name} factory must be synchronous`);\n const facade: Record<string, unknown> = Object.create(null);\n for (const operation of definition.exposes) {\n const parts = contractPath(operation);\n const { value, receiver } = lookup(implementation, parts);\n if (typeof value !== 'function') throw new Error(`Unresolved implementation ${name}.${operation}`);\n project(facade, parts, (...args: unknown[]) => {\n if (disposed) throw new Error('Domain graph is disposed');\n const policy = definition.access?.[operation];\n if (policy && options.authorize?.(policy) !== true) throw new ClientPolicyDenied(policy, `${name}.${operation}`);\n return Reflect.apply(value, receiver, args);\n });\n }\n domains[name] = freeze(facade);\n contexts[name] = Object.freeze({ ...dependencies, [name]: domains[name] });\n building.delete(name);\n return domains[name]!;\n };\n for (const definition of definitions) build(definition.name);\n freeze(domains);\n });\n return {\n domains,\n forDomain(name) { if (disposed) throw new Error('Domain graph is disposed'); if (!contexts[name]) throw new Error(`Unknown domain context ${name}`); return contexts[name]!; },\n dispose() { if (disposed) return; disposed = true; owned.dispose(); }\n };\n}\n"]}
|
package/dist/parser/index.d.ts
CHANGED
|
@@ -25,11 +25,25 @@ export interface View extends Location {
|
|
|
25
25
|
default: boolean;
|
|
26
26
|
body: (State | Element)[];
|
|
27
27
|
}
|
|
28
|
+
export interface ContractEntry extends Location {
|
|
29
|
+
name: string;
|
|
30
|
+
value?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface Contract extends Location {
|
|
33
|
+
kind: 'domain' | 'presentation' | 'destination' | 'application';
|
|
34
|
+
name: string;
|
|
35
|
+
fields: Record<string, ContractEntry>;
|
|
36
|
+
blocks: Record<string, ContractEntry[]>;
|
|
37
|
+
}
|
|
28
38
|
export interface Module {
|
|
29
39
|
source: string;
|
|
30
40
|
filename: string;
|
|
31
41
|
imports: Expression[];
|
|
32
42
|
views: View[];
|
|
43
|
+
contracts?: Contract[];
|
|
44
|
+
}
|
|
45
|
+
export interface ParsedModule extends Module {
|
|
46
|
+
contracts: Contract[];
|
|
33
47
|
}
|
|
34
48
|
export declare class HeshError extends Error {
|
|
35
49
|
readonly source: string;
|
|
@@ -44,4 +58,4 @@ export interface Token extends Location {
|
|
|
44
58
|
kind: ts.SyntaxKind;
|
|
45
59
|
}
|
|
46
60
|
export declare function tokenize(source: string, filename?: string): Token[];
|
|
47
|
-
export declare function parse(source: string, filename?: string):
|
|
61
|
+
export declare function parse(source: string, filename?: string): ParsedModule;
|
package/dist/parser/index.js
CHANGED
|
@@ -169,8 +169,78 @@ export function parse(source, filename = 'component.hesh') {
|
|
|
169
169
|
take(';');
|
|
170
170
|
return { kind: 'element', name: name.text, start: name.start, end: tokens[at - 1].end, value, handler, async, children };
|
|
171
171
|
};
|
|
172
|
-
const
|
|
172
|
+
const contract = () => {
|
|
173
|
+
const kind = take();
|
|
174
|
+
const name = identifier();
|
|
175
|
+
take('{');
|
|
176
|
+
const result = { kind: kind.text, name: name.text, start: kind.start, end: 0, fields: Object.create(null), blocks: Object.create(null) };
|
|
177
|
+
const schemas = {
|
|
178
|
+
domain: { implementation: 'string', owns: 'list', exposes: 'list', consumes: 'list', access: 'map' },
|
|
179
|
+
presentation: { html: 'string', logic: 'string', styles: 'string', bindings: 'list', actions: 'list' },
|
|
180
|
+
destination: { url: 'string', input: 'map', presentation: 'identifier', access: 'string' },
|
|
181
|
+
application: { domains: 'list', destinations: 'list' }
|
|
182
|
+
};
|
|
183
|
+
const path = () => {
|
|
184
|
+
const first = identifier();
|
|
185
|
+
let name = first.text;
|
|
186
|
+
while (peek() === '.') {
|
|
187
|
+
take();
|
|
188
|
+
name += '.' + identifier().text;
|
|
189
|
+
}
|
|
190
|
+
for (const part of name.split('.'))
|
|
191
|
+
if (['__proto__', 'prototype', 'constructor'].includes(part))
|
|
192
|
+
fail('Unsafe contract identifier', first.start);
|
|
193
|
+
return { name, start: first.start, end: tokens[at - 1].end };
|
|
194
|
+
};
|
|
195
|
+
const string = () => {
|
|
196
|
+
const token = take();
|
|
197
|
+
if (token.kind !== ts.SyntaxKind.StringLiteral)
|
|
198
|
+
fail('Expected quoted string', token.start);
|
|
199
|
+
const parsed = ts.createSourceFile('literal.js', token.text, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
|
|
200
|
+
return parsed.statements[0].expression.text;
|
|
201
|
+
};
|
|
202
|
+
while (peek() && peek() !== '}') {
|
|
203
|
+
const key = take();
|
|
204
|
+
const type = Object.hasOwn(schemas[result.kind], key.text) ? schemas[result.kind][key.text] : undefined;
|
|
205
|
+
if (!type)
|
|
206
|
+
fail(`Unknown ${result.kind} field ${key.text}`, key.start);
|
|
207
|
+
if (result.fields[key.text] || result.blocks[key.text])
|
|
208
|
+
fail(`Duplicate field ${key.text}`, key.start);
|
|
209
|
+
if (type === 'string' || type === 'identifier') {
|
|
210
|
+
take(':');
|
|
211
|
+
const value = type === 'string' ? string() : identifier().text;
|
|
212
|
+
const end = take(';').end;
|
|
213
|
+
result.fields[key.text] = { name: key.text, value, start: key.start, end };
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
take('{');
|
|
217
|
+
const entries = [];
|
|
218
|
+
const seen = new Set();
|
|
219
|
+
while (peek() && peek() !== '}') {
|
|
220
|
+
const entry = path();
|
|
221
|
+
if (seen.has(entry.name))
|
|
222
|
+
fail(`Duplicate entry ${entry.name}`, entry.start);
|
|
223
|
+
seen.add(entry.name);
|
|
224
|
+
if (type === 'map') {
|
|
225
|
+
take(':');
|
|
226
|
+
entry.value = key.text === 'input' ? identifier().text : string();
|
|
227
|
+
}
|
|
228
|
+
entry.end = take(';').end;
|
|
229
|
+
entries.push(entry);
|
|
230
|
+
}
|
|
231
|
+
take('}');
|
|
232
|
+
result.blocks[key.text] = entries;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
result.end = take('}').end;
|
|
236
|
+
return result;
|
|
237
|
+
};
|
|
238
|
+
const module = { source, filename, imports: [], views: [], contracts: [] };
|
|
173
239
|
while (tokens[at]) {
|
|
240
|
+
if (['domain', 'presentation', 'destination', 'application'].includes(peek() ?? '')) {
|
|
241
|
+
module.contracts.push(contract());
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
174
244
|
if (peek() === 'import') {
|
|
175
245
|
if (module.views.length)
|
|
176
246
|
fail('Imports must precede views');
|
|
@@ -211,8 +281,8 @@ export function parse(source, filename = 'component.hesh') {
|
|
|
211
281
|
const end = take('}').end;
|
|
212
282
|
module.views.push({ name, body, start, end, exported, default: isDefault });
|
|
213
283
|
}
|
|
214
|
-
if (!module.views.length)
|
|
215
|
-
fail('Expected at least one view');
|
|
284
|
+
if (!module.views.length && !module.contracts.length)
|
|
285
|
+
fail('Expected at least one view or architecture declaration');
|
|
216
286
|
return module;
|
|
217
287
|
}
|
|
218
288
|
//# sourceMappingURL=index.js.map
|
package/dist/parser/index.js.map
CHANGED
|
@@ -1 +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"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/parser/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAe5B,MAAM,OAAO,SAAU,SAAQ,KAAK;IAGI;IAAyB;IAAyB;IAF/E,IAAI,CAAS;IACb,MAAM,CAAS;IACxB,YAAY,OAAe,EAAW,MAAc,EAAW,MAAc,EAAW,WAAW,gBAAgB;QACjH,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,MAAM,GAAG,CAAC,CAAC;QAC7D,KAAK,CAAC,GAAG,QAAQ,IAAI,IAAI,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC,CAAC;QAHf,WAAM,GAAN,MAAM,CAAQ;QAAW,WAAM,GAAN,MAAM,CAAQ;QAAW,aAAQ,GAAR,QAAQ,CAAmB;QAIjH,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IAClE,CAAC;CACF;AAED,MAAM,UAAU,QAAQ,CAAC,MAAc,EAAE,QAAQ,GAAG,gBAAgB;IAClE,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,uPAAuP,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7R,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,MAAM,IAAI,GAAG,CAAC,OAAe,EAAE,MAAc,EAAS,EAAE,GAAG,MAAM,IAAI,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACrH,OAAO,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,EAAE,CAAC;QACjB,MAAM,EAAE,GAAG,MAAM,CAAC,EAAE,CAAE,CAAC;QACvB,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;YAAC,EAAE,EAAE,CAAC;YAAC,SAAS;QAAC,CAAC;QACtC,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;YAAC,OAAO,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,EAAE,CAAC,KAAK,IAAI;gBAAE,EAAE,EAAE,CAAC;YAAC,SAAS;QAAC,CAAC;QACtG,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;YACzC,IAAI,GAAG,GAAG,CAAC;gBAAE,IAAI,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC;YAC9C,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;YAAC,SAAS;QACzB,CAAC;QACD,IAAI,IAAI,GAAG,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;QACjC,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YAC3C,MAAM,KAAK,GAAG,EAAE,CAAC;YACjB,EAAE,EAAE,CAAC;YAAC,IAAI,MAAM,GAAG,KAAK,CAAC;YACzB,OAAO,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;gBAC1B,MAAM,OAAO,GAAG,MAAM,CAAC,EAAE,EAAE,CAAE,CAAC;gBAC9B,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;oBAAC,EAAE,EAAE,CAAC;oBAAC,SAAS;gBAAC,CAAC;gBACzC,IAAI,KAAK,KAAK,GAAG,IAAI,OAAO,KAAK,GAAG,IAAI,MAAM,CAAC,EAAE,CAAC,KAAK,GAAG;oBAAE,IAAI,CAAC,wEAAwE,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;gBACnJ,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;oBAAC,MAAM,GAAG,IAAI,CAAC;oBAAC,MAAM;gBAAC,CAAC;gBAChD,IAAI,KAAK,KAAK,GAAG,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,IAAI,CAAC;oBAAE,IAAI,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;YAClG,CAAC;YACD,IAAI,CAAC,MAAM;gBAAE,IAAI,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;YAChD,IAAI,GAAG,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,6BAA6B,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC;QACnG,CAAC;aAAM,IAAI,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;YACxC,EAAE,EAAE,CAAC;YACL,OAAO,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,iCAAiC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAE,CAAC;gBAAE,EAAE,EAAE,CAAC;YACvF,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;QAClG,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;YACzB,EAAE,EAAE,CAAC;YACL,OAAO,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAE,CAAC;gBAAE,EAAE,EAAE,CAAC;YAC7D,IAAI,GAAG,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC;QACtC,CAAC;aAAM,CAAC;YACN,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;YAC1Q,EAAE,IAAI,QAAQ,EAAE,MAAM,IAAI,CAAC,CAAC;QAC9B,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,MAAc,EAAE,QAAQ,GAAG,gBAAgB;IAC/D,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC1C,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC;IACpC,MAAM,IAAI,GAAG,CAAC,OAAe,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC,EAAE,KAAK,IAAI,MAAM,CAAC,MAAM,EAAS,EAAE,GAAG,MAAM,IAAI,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAClJ,MAAM,IAAI,GAAG,CAAC,IAAa,EAAS,EAAE;QACpC,MAAM,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;QACzB,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;YAAE,IAAI,CAAC,YAAY,IAAI,IAAI,OAAO,cAAc,IAAI,EAAE,IAAI,aAAa,EAAE,CAAC,CAAC;QACpI,EAAE,EAAE,CAAC;QAAC,OAAO,KAAM,CAAC;IACtB,CAAC,CAAC;IACF,MAAM,UAAU,GAAG,GAAG,EAAE;QACtB,MAAM,KAAK,GAAG,IAAI,EAAE,CAAC;QACrB,IAAI,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC,UAAU,CAAC,UAAU;YAAE,IAAI,CAAC,qBAAqB,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QACtF,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;YAAE,IAAI,CAAC,wCAAwC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9F,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IACF,MAAM,UAAU,GAAG,CAAC,IAAc,EAAc,EAAE;QAChD,MAAM,KAAK,GAAG,MAAM,CAAC,EAAE,CAAC,EAAE,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC;QACjD,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,KAAK,GAA2B,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;QACvE,OAAO,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;YAClB,MAAM,KAAK,GAAG,MAAM,CAAC,EAAE,CAAE,CAAC;YAC1B,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;gBAAE,MAAM;YACtD,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC,CAAC;iBACjD,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9C,IAAI,KAAK,CAAC,GAAG,EAAE,KAAK,KAAK,CAAC,IAAI;oBAAE,IAAI,CAAC,uBAAuB,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;YAC7E,CAAC;YACD,EAAE,EAAE,CAAC;QACP,CAAC;QACD,IAAI,KAAK,CAAC,MAAM;YAAE,IAAI,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;QACzD,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,CAAC,EAAE,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC;QAC/C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE;YAAE,IAAI,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;QACzE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACpE,CAAC,CAAC;IACF,MAAM,OAAO,GAAG,GAAY,EAAE;QAC5B,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,KAA6B,EAAE,OAA+B,EAAE,KAAK,GAAG,KAAK,CAAC;QAClF,IAAI,IAAI,EAAE,KAAK,GAAG;YAAE,KAAK,GAAG,UAAU,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QACnD,IAAI,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACnB,IAAI,CAAC,GAAG,CAAC,CAAC;YACV,IAAI,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC;gBAAC,IAAI,EAAE,CAAC;gBAAC,KAAK,GAAG,IAAI,CAAC;YAAC,CAAC;YACjD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;YAC5B,IAAI,KAAK,GAAG,CAAC,CAAC;YACd,OAAO,MAAM,CAAC,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC;gBAC3B,IAAI,IAAI,EAAE,KAAK,GAAG;oBAAE,KAAK,EAAE,CAAC;gBAC5B,IAAI,IAAI,EAAE,KAAK,GAAG;oBAAE,KAAK,EAAE,CAAC;gBAC5B,IAAI,KAAK;oBAAE,EAAE,EAAE,CAAC;YAClB,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBAAE,IAAI,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;YACzD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC;YAC5B,OAAO,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;QAC7D,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,IAAI,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YAAC,IAAI,EAAE,CAAC;YAAC,OAAO,IAAI,EAAE,IAAI,IAAI,EAAE,KAAK,GAAG;gBAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;YAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAAC,IAAI,IAAI,EAAE,KAAK,GAAG;gBAAE,IAAI,EAAE,CAAC;QAAC,CAAC;;YAC5H,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,CAAE,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAC5H,CAAC,CAAC;IACF,MAAM,QAAQ,GAAG,GAAa,EAAE;QAC9B,MAAM,IAAI,GAAG,IAAI,EAAE,CAAC;QAAC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAa,EAAE,IAAI,EAAE,IAAI,CAAC,IAAwB,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACvK,MAAM,OAAO,GAAqD;YAChE,MAAM,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE;YACpG,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE;YACtG,WAAW,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE;YAC1F,WAAW,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE;SACvD,CAAC;QACF,MAAM,IAAI,GAAG,GAAkB,EAAE;YAC/B,MAAM,KAAK,GAAG,UAAU,EAAE,CAAC;YAAC,IAAI,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;YAClD,OAAO,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;gBAAC,IAAI,EAAE,CAAC;gBAAC,IAAI,IAAI,GAAG,GAAG,UAAU,EAAE,CAAC,IAAI,CAAC;YAAC,CAAC;YACnE,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;gBAAE,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;oBAAE,IAAI,CAAC,4BAA4B,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;YAClJ,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,CAAE,CAAC,GAAG,EAAE,CAAC;QAChE,CAAC,CAAC;QACF,MAAM,MAAM,GAAG,GAAG,EAAE;YAClB,MAAM,KAAK,GAAG,IAAI,EAAE,CAAC;YACrB,IAAI,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC,UAAU,CAAC,aAAa;gBAAE,IAAI,CAAC,wBAAwB,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;YAC5F,MAAM,MAAM,GAAG,EAAE,CAAC,gBAAgB,CAAC,YAAY,EAAE,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YAC7G,OAAS,MAAM,CAAC,UAAU,CAAC,CAAC,CAA4B,CAAC,UAA+B,CAAC,IAAI,CAAC;QAChG,CAAC,CAAC;QACF,OAAO,IAAI,EAAE,IAAI,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,IAAI,EAAE,CAAC;YAAC,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC5H,IAAI,CAAC,IAAI;gBAAE,IAAI,CAAC,WAAW,MAAM,CAAC,IAAI,UAAU,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;YACvE,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,IAAI,CAAC,mBAAmB,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;YACvG,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;gBAC/C,IAAI,CAAC,GAAG,CAAC,CAAC;gBAAC,MAAM,KAAK,GAAG,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC;gBAAC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;gBACrG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC;YAC7E,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,GAAG,CAAC,CAAC;gBAAC,MAAM,OAAO,GAAoB,EAAE,CAAC;gBAAC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;gBAC/E,OAAO,IAAI,EAAE,IAAI,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;oBAChC,MAAM,KAAK,GAAG,IAAI,EAAE,CAAC;oBACrB,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;wBAAE,IAAI,CAAC,mBAAmB,KAAK,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;oBAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBACnG,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;wBAAC,IAAI,CAAC,GAAG,CAAC,CAAC;wBAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;oBAAC,CAAC;oBACrG,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;oBAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACjD,CAAC;gBACD,IAAI,CAAC,GAAG,CAAC,CAAC;gBAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC;YAC/C,CAAC;QACH,CAAC;QACD,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;QAC3B,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IACF,MAAM,MAAM,GAAiB,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IACzF,OAAO,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;QAClB,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YAAC,SAAS;QAAC,CAAC;QACrI,IAAI,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC;YACxB,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM;gBAAE,IAAI,CAAC,4BAA4B,CAAC,CAAC;YAC5D,MAAM,KAAK,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC;YAC3B,OAAO,IAAI,EAAE,IAAI,IAAI,EAAE,KAAK,GAAG;gBAAE,IAAI,EAAE,CAAC;YACxC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;YAC1B,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YAAC,SAAS;QAClF,CAAC;QACD,IAAI,QAAQ,GAAG,KAAK,EAAE,SAAS,GAAG,KAAK,CAAC;QACxC,IAAI,IAAI,EAAE,KAAK,QAAQ,EAAE,CAAC;YAAC,IAAI,EAAE,CAAC;YAAC,QAAQ,GAAG,IAAI,CAAC;QAAC,CAAC;QACrD,IAAI,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;YAAC,IAAI,EAAE,CAAC;YAAC,SAAS,GAAG,IAAI,CAAC;YAAC,IAAI,CAAC,QAAQ;gBAAE,IAAI,CAAC,yBAAyB,CAAC,CAAC;QAAC,CAAC;QACvG,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC;QACjC,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC,IAAI,CAAC;QAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1C,MAAM,IAAI,GAAwB,EAAE,CAAC;QACrC,OAAO,IAAI,EAAE,IAAI,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YAChC,IAAI,IAAI,EAAE,KAAK,OAAO,IAAI,MAAM,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,KAAK,GAAG,EAAE,CAAC;gBACvD,MAAM,IAAI,GAAG,IAAI,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,OAAgB,CAAC,CAAC,CAAC,CAAC,OAAgB,CAAC;gBAChF,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;gBAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAAC,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;gBACjG,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YACtE,CAAC;;gBAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9B,CAAC;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC;QAC1B,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM;QAAE,IAAI,CAAC,wDAAwD,CAAC,CAAC;IACrH,OAAO,MAAM,CAAC;AAChB,CAAC","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 ContractEntry extends Location { name: string; value?: string }\nexport interface Contract extends Location {\n kind: 'domain' | 'presentation' | 'destination' | 'application';\n name: string;\n fields: Record<string, ContractEntry>;\n blocks: Record<string, ContractEntry[]>;\n}\nexport interface Module { source: string; filename: string; imports: Expression[]; views: View[]; contracts?: Contract[] }\nexport interface ParsedModule extends Module { contracts: Contract[] }\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'): ParsedModule {\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 contract = (): Contract => {\n const kind = take(); const name = identifier(); take('{');\n const result: Contract = { kind: kind.text as Contract['kind'], name: name.text, start: kind.start, end: 0, fields: Object.create(null), blocks: Object.create(null) };\n const schemas: Record<Contract['kind'], Record<string, string>> = {\n domain: { implementation: 'string', owns: 'list', exposes: 'list', consumes: 'list', access: 'map' },\n presentation: { html: 'string', logic: 'string', styles: 'string', bindings: 'list', actions: 'list' },\n destination: { url: 'string', input: 'map', presentation: 'identifier', access: 'string' },\n application: { domains: 'list', destinations: 'list' }\n };\n const path = (): ContractEntry => {\n const first = identifier(); let name = first.text;\n while (peek() === '.') { take(); name += '.' + identifier().text; }\n for (const part of name.split('.')) if (['__proto__', 'prototype', 'constructor'].includes(part)) fail('Unsafe contract identifier', first.start);\n return { name, start: first.start, end: tokens[at - 1]!.end };\n };\n const string = () => {\n const token = take();\n if (token.kind !== ts.SyntaxKind.StringLiteral) fail('Expected quoted string', token.start);\n const parsed = ts.createSourceFile('literal.js', token.text, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);\n return ((parsed.statements[0] as ts.ExpressionStatement).expression as ts.StringLiteral).text;\n };\n while (peek() && peek() !== '}') {\n const key = take(); const type = Object.hasOwn(schemas[result.kind], key.text) ? schemas[result.kind][key.text] : undefined;\n if (!type) fail(`Unknown ${result.kind} field ${key.text}`, key.start);\n if (result.fields[key.text] || result.blocks[key.text]) fail(`Duplicate field ${key.text}`, key.start);\n if (type === 'string' || type === 'identifier') {\n take(':'); const value = type === 'string' ? string() : identifier().text; const end = take(';').end;\n result.fields[key.text] = { name: key.text, value, start: key.start, end };\n } else {\n take('{'); const entries: ContractEntry[] = []; const seen = new Set<string>();\n while (peek() && peek() !== '}') {\n const entry = path();\n if (seen.has(entry.name)) fail(`Duplicate entry ${entry.name}`, entry.start); seen.add(entry.name);\n if (type === 'map') { take(':'); entry.value = key.text === 'input' ? identifier().text : string(); }\n entry.end = take(';').end; entries.push(entry);\n }\n take('}'); result.blocks[key.text] = entries;\n }\n }\n result.end = take('}').end;\n return result;\n };\n const module: ParsedModule = { source, filename, imports: [], views: [], contracts: [] };\n while (tokens[at]) {\n if (['domain', 'presentation', 'destination', 'application'].includes(peek() ?? '')) { module.contracts.push(contract()); continue; }\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 && !module.contracts.length) fail('Expected at least one view or architecture declaration');\n return module;\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { nativeElement as element, text, attribute, listen } from '../dom/index.js';
|
|
2
|
+
export { element, text, attribute, listen };
|
|
3
|
+
export interface PresentationState {
|
|
4
|
+
bindings: Record<string, unknown>;
|
|
5
|
+
actions: Record<string, (event: Event) => unknown>;
|
|
6
|
+
}
|
|
7
|
+
export declare function readBinding(bindings: Record<string, unknown>, path: string): unknown;
|
|
8
|
+
export declare function bindValue(node: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement, bindings: Record<string, unknown>, name: string): void;
|
|
9
|
+
export declare function bindAction(node: HTMLElement, state: PresentationState, name: string): void;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { effect } from '../reactivity/index.js';
|
|
2
|
+
import { nativeElement as element, text, attribute, listen } from '../dom/index.js';
|
|
3
|
+
export { element, text, attribute, listen };
|
|
4
|
+
export function readBinding(bindings, path) {
|
|
5
|
+
const unwrap = (value) => {
|
|
6
|
+
if (typeof value === 'function')
|
|
7
|
+
return value();
|
|
8
|
+
if (value && typeof value.get === 'function')
|
|
9
|
+
return value.get();
|
|
10
|
+
return value;
|
|
11
|
+
};
|
|
12
|
+
let value = bindings;
|
|
13
|
+
for (const part of path.split('.')) {
|
|
14
|
+
if (['__proto__', 'constructor', 'prototype'].includes(part))
|
|
15
|
+
throw new Error('Unsafe binding path');
|
|
16
|
+
value = unwrap(value)?.[part];
|
|
17
|
+
}
|
|
18
|
+
return unwrap(value);
|
|
19
|
+
}
|
|
20
|
+
export function bindValue(node, bindings, name) {
|
|
21
|
+
const state = bindings[name];
|
|
22
|
+
if (typeof state?.get !== 'function' || typeof state.set !== 'function')
|
|
23
|
+
throw new Error(`h-value ${name} requires a writable signal`);
|
|
24
|
+
effect(() => { const value = String(state.get() ?? ''); if (node.value !== value)
|
|
25
|
+
node.value = value; });
|
|
26
|
+
listen(node, 'input', () => state.set(node.value));
|
|
27
|
+
}
|
|
28
|
+
export function bindAction(node, state, name) {
|
|
29
|
+
const handler = state.actions[name];
|
|
30
|
+
if (typeof handler !== 'function')
|
|
31
|
+
throw new Error(`Missing presentation action ${name}`);
|
|
32
|
+
listen(node, node.tagName === 'FORM' ? 'submit' : 'click', event => {
|
|
33
|
+
if (node.tagName === 'FORM')
|
|
34
|
+
event.preventDefault();
|
|
35
|
+
return handler.call(state.actions, event);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/presentation/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACpF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;AAE5C,MAAM,UAAU,WAAW,CAAC,QAAiC,EAAE,IAAY;IACzE,MAAM,MAAM,GAAG,CAAC,KAAc,EAAW,EAAE;QACzC,IAAI,OAAO,KAAK,KAAK,UAAU;YAAE,OAAO,KAAK,EAAE,CAAC;QAChD,IAAI,KAAK,IAAI,OAAQ,KAA2B,CAAC,GAAG,KAAK,UAAU;YAAE,OAAQ,KAA4B,CAAC,GAAG,EAAE,CAAC;QAChH,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IACF,IAAI,KAAK,GAAY,QAAQ,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACnC,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACrG,KAAK,GAAI,MAAM,CAAC,KAAK,CAAoC,EAAE,CAAC,IAAI,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AACD,MAAM,UAAU,SAAS,CAAC,IAAgE,EAAE,QAAiC,EAAE,IAAY;IACzI,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAoD,CAAC;IAChF,IAAI,OAAO,KAAK,EAAE,GAAG,KAAK,UAAU,IAAI,OAAO,KAAK,CAAC,GAAG,KAAK,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,WAAW,IAAI,6BAA6B,CAAC,CAAC;IACvI,MAAM,CAAC,GAAG,EAAE,GAAG,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK;QAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1G,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,GAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;AACtD,CAAC;AACD,MAAM,UAAU,UAAU,CAAC,IAAiB,EAAE,KAAwB,EAAE,IAAY;IAClF,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,OAAO,OAAO,KAAK,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,IAAI,EAAE,CAAC,CAAC;IAC1F,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE;QACjE,IAAI,IAAI,CAAC,OAAO,KAAK,MAAM;YAAE,KAAK,CAAC,cAAc,EAAE,CAAC;QACpD,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { effect } from '../reactivity/index.js';\nimport { nativeElement as element, text, attribute, listen } from '../dom/index.js';\nexport { element, text, attribute, listen };\nexport interface PresentationState { bindings: Record<string, unknown>; actions: Record<string, (event: Event) => unknown> }\nexport function readBinding(bindings: Record<string, unknown>, path: string): unknown {\n const unwrap = (value: unknown): unknown => {\n if (typeof value === 'function') return value();\n if (value && typeof (value as { get?: unknown }).get === 'function') return (value as { get(): unknown }).get();\n return value;\n };\n let value: unknown = bindings;\n for (const part of path.split('.')) {\n if (['__proto__', 'constructor', 'prototype'].includes(part)) throw new Error('Unsafe binding path');\n value = (unwrap(value) as Record<string, unknown> | null)?.[part];\n }\n return unwrap(value);\n}\nexport function bindValue(node: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement, bindings: Record<string, unknown>, name: string): void {\n const state = bindings[name] as { get?(): unknown; set?(value: unknown): void };\n if (typeof state?.get !== 'function' || typeof state.set !== 'function') throw new Error(`h-value ${name} requires a writable signal`);\n effect(() => { const value = String(state.get!() ?? ''); if (node.value !== value) node.value = value; });\n listen(node, 'input', () => state.set!(node.value));\n}\nexport function bindAction(node: HTMLElement, state: PresentationState, name: string): void {\n const handler = state.actions[name];\n if (typeof handler !== 'function') throw new Error(`Missing presentation action ${name}`);\n listen(node, node.tagName === 'FORM' ? 'submit' : 'click', event => {\n if (node.tagName === 'FORM') event.preventDefault();\n return handler.call(state.actions, event);\n });\n}\n"]}
|
package/dist/vite/index.d.ts
CHANGED
package/dist/vite/index.js
CHANGED
|
@@ -1,17 +1,51 @@
|
|
|
1
1
|
import { compile } from '../compiler/index.js';
|
|
2
|
-
|
|
2
|
+
import { createArchitectureCompiler, writeProject, formatDiagnostic } from '../architecture/index.js';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
export function hesh(options = {}) {
|
|
3
5
|
let production = false;
|
|
6
|
+
const architecture = createArchitectureCompiler();
|
|
7
|
+
let project;
|
|
8
|
+
let watched = [];
|
|
9
|
+
let warn;
|
|
10
|
+
const check = () => {
|
|
11
|
+
if (!project)
|
|
12
|
+
return;
|
|
13
|
+
const result = architecture.check(project);
|
|
14
|
+
if (result.diagnostics.some(item => item.severity === 'error'))
|
|
15
|
+
throw new Error(result.diagnostics.map(formatDiagnostic).join('\n'));
|
|
16
|
+
for (const item of result.diagnostics)
|
|
17
|
+
if (item.severity === 'warning')
|
|
18
|
+
warn?.(formatDiagnostic(item));
|
|
19
|
+
writeProject(result);
|
|
20
|
+
watched = result.files;
|
|
21
|
+
};
|
|
4
22
|
return {
|
|
5
23
|
name: 'heshjs', enforce: 'pre',
|
|
6
24
|
config() { return { appType: 'spa' }; },
|
|
7
|
-
configResolved(config) {
|
|
25
|
+
configResolved(config) {
|
|
26
|
+
production = config.command === 'build';
|
|
27
|
+
warn = message => config.logger.warn(message);
|
|
28
|
+
if (options.architecture) {
|
|
29
|
+
project = { ...options.architecture, root: resolve(config.root, options.architecture.root) };
|
|
30
|
+
check();
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
buildStart() { for (const file of watched)
|
|
34
|
+
this.addWatchFile(file); },
|
|
8
35
|
transform(source, id) {
|
|
36
|
+
if (project && /(?:^|\/)server(?:\/|$)|\.server\.[cm]?[jt]s$/.test(id.replace(/\\/g, '/')))
|
|
37
|
+
throw new Error(`HESH-SEC-001 Server-only module cannot enter the browser bundle: ${id}`);
|
|
9
38
|
if (!id.endsWith('.hesh'))
|
|
10
39
|
return;
|
|
11
40
|
const result = compile(source, { filename: id, development: !production });
|
|
12
41
|
return { code: result.code, map: null };
|
|
13
42
|
},
|
|
14
43
|
handleHotUpdate(context) {
|
|
44
|
+
if (project && (watched.includes(context.file.replace(/\\/g, '/').toLowerCase()) || watched.includes(context.file.replace(/\\/g, '/')))) {
|
|
45
|
+
check();
|
|
46
|
+
context.server.ws.send({ type: 'full-reload' });
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
15
49
|
if (context.file.endsWith('.hesh')) {
|
|
16
50
|
context.server.ws.send({ type: 'full-reload' });
|
|
17
51
|
return [];
|
package/dist/vite/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,MAAM,UAAU,IAAI;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Plugin } from 'vite';\nimport { compile } from '../compiler/index.js';\nimport { createArchitectureCompiler, writeProject, formatDiagnostic, type ProjectOptions } from '../architecture/index.js';\nimport { resolve } from 'node:path';\nexport function hesh(options: { architecture?: ProjectOptions } = {}): Plugin {\n let production = false;\n const architecture = createArchitectureCompiler();\n let project: ProjectOptions | undefined;\n let watched: string[] = [];\n let warn: ((message: string) => void) | undefined;\n const check = () => {\n if (!project) return;\n const result = architecture.check(project);\n if (result.diagnostics.some(item => item.severity === 'error')) throw new Error(result.diagnostics.map(formatDiagnostic).join('\\n'));\n for (const item of result.diagnostics) if (item.severity === 'warning') warn?.(formatDiagnostic(item));\n writeProject(result); watched = result.files;\n };\n return {\n name: 'heshjs', enforce: 'pre',\n config() { return { appType: 'spa' }; },\n configResolved(config) {\n production = config.command === 'build';\n warn = message => config.logger.warn(message);\n if (options.architecture) { project = { ...options.architecture, root: resolve(config.root, options.architecture.root) }; check(); }\n },\n buildStart() { for (const file of watched) this.addWatchFile(file); },\n transform(source, id) {\n if (project && /(?:^|\\/)server(?:\\/|$)|\\.server\\.[cm]?[jt]s$/.test(id.replace(/\\\\/g, '/'))) throw new Error(`HESH-SEC-001 Server-only module cannot enter the browser bundle: ${id}`);\n if (!id.endsWith('.hesh')) return;\n const result = compile(source, { filename: id, development: !production });\n return { code: result.code, map: null };\n },\n handleHotUpdate(context) {\n if (project && (watched.includes(context.file.replace(/\\\\/g, '/').toLowerCase()) || watched.includes(context.file.replace(/\\\\/g, '/')))) {\n check(); context.server.ws.send({ type: 'full-reload' }); return [];\n }\n if (context.file.endsWith('.hesh')) { context.server.ws.send({ type: 'full-reload' }); return []; }\n }\n };\n}\n"]}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Architecture declaration grammar (experimental)
|
|
2
|
+
|
|
3
|
+
These declarations extend the existing Hesh lexer/parser. A file may contain
|
|
4
|
+
views or architecture declarations; project compilation resolves architecture.
|
|
5
|
+
Imports in architecture files reference other .hesh descriptor files explicitly.
|
|
6
|
+
|
|
7
|
+
```ebnf
|
|
8
|
+
domain = "domain", Identifier, "{",
|
|
9
|
+
"implementation", ":", String, ";",
|
|
10
|
+
[ "owns", "{", { Identifier, ";" }, "}" ],
|
|
11
|
+
"exposes", "{", { Path, ";" }, "}",
|
|
12
|
+
[ "consumes", "{", { Path, ";" }, "}" ],
|
|
13
|
+
[ "access", "{", { Path, ":", String, ";" }, "}" ], "}" ;
|
|
14
|
+
presentation = "presentation", Identifier, "{",
|
|
15
|
+
"html", ":", String, ";", "logic", ":", String, ";",
|
|
16
|
+
[ "styles", ":", String, ";" ],
|
|
17
|
+
"bindings", "{", { Identifier, ";" }, "}",
|
|
18
|
+
"actions", "{", { Identifier, ";" }, "}", "}" ;
|
|
19
|
+
destination = "destination", Identifier, "{",
|
|
20
|
+
"url", ":", String, ";",
|
|
21
|
+
"input", "{", { Identifier, ":", ("string" | "number" | "boolean"), ";" }, "}",
|
|
22
|
+
"presentation", ":", Identifier, ";", [ "access", ":", String, ";" ], "}" ;
|
|
23
|
+
application = "application", Identifier, "{",
|
|
24
|
+
"domains", "{", { Identifier, ";" }, "}",
|
|
25
|
+
"destinations", "{", { Identifier, ";" }, "}", "}" ;
|
|
26
|
+
Path = Identifier, { ".", Identifier } ;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Fields/blocks can appear in any order, once each. Unknown/duplicate fields are
|
|
30
|
+
errors. Strings use ordinary JS string escaping. Identifiers cannot be prototype
|
|
31
|
+
mutation names. Locations are retained for diagnostics. Semicolons are required.
|
|
32
|
+
|
|
33
|
+
`implementation` and `logic` have form `./module.js#exportName` (or .ts/.mjs).
|
|
34
|
+
A domain factory takes a dependencies object and returns its actual API. Every
|
|
35
|
+
exposed dotted operation must exist and be callable according to the TS checker;
|
|
36
|
+
every consumed path is Domain.operation and must be publicly exposed. An access
|
|
37
|
+
entry must name an exposed operation. `owns` is ownership metadata, not a generated
|
|
38
|
+
entity/validation/database implementation. The runtime passes only declared
|
|
39
|
+
consumed operations and exposes only the validated public facade.
|
|
40
|
+
|
|
41
|
+
A presentation factory receives `{ domains, input, location, signal }` and returns
|
|
42
|
+
`{ bindings, actions }`. bindings values may be signals/readers/ordinary values;
|
|
43
|
+
actions are callable. Both declared names and nested template paths are checked
|
|
44
|
+
against its inferred JS/TS return shape. HTML text `{product.name}` is escaped via
|
|
45
|
+
text nodes. `h-bind="product.name"` binds textContent (cannot have child content),
|
|
46
|
+
`h-action="reserve"` listens to click (submit on a form), `h-value="quantity"`
|
|
47
|
+
binds a writable signal to an input/textarea/select, and
|
|
48
|
+
`h-destination="ProductDetails" h-input-id="product.id"` binds a native anchor to
|
|
49
|
+
a validated destination. Other normal HTML attributes/semantics are preserved.
|
|
50
|
+
No arbitrary JS expressions, raw HTML, inline event attributes, script elements,
|
|
51
|
+
h-each, h-key or unknown h-* directives are accepted in this first milestone.
|
|
52
|
+
Use normal CSS files; template scoping and keyed template lists remain deferred.
|
|
53
|
+
|
|
54
|
+
Destination URL params must exactly match input names. Values are encoded/decoded
|
|
55
|
+
once; number inputs must be finite and booleans serialize as true/false. Duplicate
|
|
56
|
+
or structurally conflicting URL destinations are errors. Generated declarations
|
|
57
|
+
provide TS input types and JS IntelliSense. Runtime validation also rejects missing,
|
|
58
|
+
extra or invalid fields in JavaScript. Destination policy names are client metadata;
|
|
59
|
+
they cannot enforce server-side authorization. A policy without an authorize
|
|
60
|
+
callback is denied rather than silently accepted.
|