@heshammesalam/heshjs 0.1.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.
Files changed (54) hide show
  1. package/README.md +19 -4
  2. package/cli.js +26 -0
  3. package/dist/architecture/diagnostics.d.ts +12 -0
  4. package/dist/architecture/diagnostics.js +8 -0
  5. package/dist/architecture/diagnostics.js.map +1 -0
  6. package/dist/architecture/html.d.ts +16 -0
  7. package/dist/architecture/html.js +132 -0
  8. package/dist/architecture/html.js.map +1 -0
  9. package/dist/architecture/index.d.ts +42 -0
  10. package/dist/architecture/index.js +418 -0
  11. package/dist/architecture/index.js.map +1 -0
  12. package/dist/compiler/index.js +2 -0
  13. package/dist/compiler/index.js.map +1 -1
  14. package/dist/destinations/index.d.ts +63 -0
  15. package/dist/destinations/index.js +185 -0
  16. package/dist/destinations/index.js.map +1 -0
  17. package/dist/dom/index.d.ts +2 -0
  18. package/dist/dom/index.js +5 -1
  19. package/dist/dom/index.js.map +1 -1
  20. package/dist/domains/index.d.ts +24 -0
  21. package/dist/domains/index.js +110 -0
  22. package/dist/domains/index.js.map +1 -0
  23. package/dist/hmvc/index.d.ts +35 -0
  24. package/dist/hmvc/index.js +49 -0
  25. package/dist/hmvc/index.js.map +1 -0
  26. package/dist/parser/index.d.ts +15 -1
  27. package/dist/parser/index.js +73 -3
  28. package/dist/parser/index.js.map +1 -1
  29. package/dist/presentation/index.d.ts +9 -0
  30. package/dist/presentation/index.js +38 -0
  31. package/dist/presentation/index.js.map +1 -0
  32. package/dist/router/index.d.ts +24 -0
  33. package/dist/router/index.js +258 -0
  34. package/dist/router/index.js.map +1 -0
  35. package/dist/vite/index.d.ts +4 -1
  36. package/dist/vite/index.js +37 -2
  37. package/dist/vite/index.js.map +1 -1
  38. package/docs/contracts.md +60 -0
  39. package/docs/domain-graph.md +205 -0
  40. package/docs/grammar.md +4 -0
  41. package/docs/hmvc.md +134 -0
  42. package/package.json +10 -3
  43. package/src/architecture/diagnostics.ts +11 -0
  44. package/src/architecture/html.ts +88 -0
  45. package/src/architecture/index.ts +283 -0
  46. package/src/compiler/index.ts +1 -0
  47. package/src/destinations/index.ts +146 -0
  48. package/src/dom/index.ts +6 -1
  49. package/src/domains/index.ts +97 -0
  50. package/src/hmvc/index.ts +71 -0
  51. package/src/parser/index.ts +55 -4
  52. package/src/presentation/index.ts +31 -0
  53. package/src/router/index.ts +171 -0
  54. package/src/vite/index.ts +25 -2
package/README.md CHANGED
@@ -1,7 +1,13 @@
1
1
  # HeshJS
2
2
 
3
- An independent JavaScript/TypeScript UI framework created by Hesham. Experimental
4
- Milestone 1 release; APIs and grammar may change.
3
+ An independent JavaScript/TypeScript UI framework created by Hesham, with History
4
+ API routing and hierarchical MVC modules plus an experimental Domain Graph layer.
5
+
6
+ Version 0.3 adds opt-in .hesh domain/presentation/destination contracts,
7
+ the Node-only `/architecture` compiler and `hesh check|graph` command, `/domains`,
8
+ `/presentation` and `/destinations` runtimes. See docs/contracts.md and
9
+ docs/domain-graph.md for the precise supported slice and its limits. The default
10
+ creator remains an HMVC starter; Domain Graph is enabled explicitly in Vite.
5
11
 
6
12
  Requires Node.js 22.12 or later.
7
13
 
@@ -13,6 +19,9 @@ npm run dev
13
19
  ```
14
20
 
15
21
  For TypeScript, use `npm create @heshammesalam/heshjs@latest my-app -- --ts`.
22
+ The starter is a working HMVC SPA with nested modules, Home/About/detail/not-found
23
+ routes and a persistent shell. Each module contains model, controller, View.hesh
24
+ and module registration. See `docs/hmvc.md` for APIs, lifecycle and deployment.
16
25
 
17
26
  ```hesh
18
27
  export default view Counter {
@@ -28,16 +37,22 @@ to your Vite plugins. Import a default `.hesh` component, then call
28
37
  `mount(Component, document.querySelector('#app'))` from `@heshammesalam/heshjs`.
29
38
 
30
39
  ESM entrypoints: `@heshammesalam/heshjs`, `@heshammesalam/heshjs/reactivity`, `@heshammesalam/heshjs/dom`, `@heshammesalam/heshjs/parser`,
31
- `@heshammesalam/heshjs/compiler`, `@heshammesalam/heshjs/vite`. JavaScript and declarations are included.
40
+ `@heshammesalam/heshjs/compiler`, `@heshammesalam/heshjs/vite`,
41
+ `@heshammesalam/heshjs/router`, `@heshammesalam/heshjs/hmvc`. JavaScript and declarations are included.
32
42
  The root entry contains runtime APIs only; compiler/TypeScript code stays out of
33
43
  the browser unless explicitly imported. Vite is an optional peer dependency.
34
44
 
35
45
  State is shallow: replace arrays/objects to notify. Component instances own DOM,
36
46
  effects and listener cleanup. Async handlers report errors but are not automatically
37
47
  canceled on unmount. Hesh edits reload the page. See `docs/grammar.md` for supported
38
- syntax. Props, conditional rendering, keyed lists, themes, routing, SSR and full
48
+ syntax. Props, conditional rendering, keyed lists, themes, SSR and full
39
49
  type checking inside `.hesh` are not yet implemented. TypeScript helpers and
40
50
  consumer APIs support strict type checking.
41
51
 
52
+ Router navigation intercepts eligible same-origin anchors and supports nested
53
+ outlets, :params, query/hash updates, back/forward and not-found handling. Parents
54
+ remain mounted across child navigation. Refreshing a deep link requires the host
55
+ to serve index.html for application paths; the scaffold documents that setup.
56
+
42
57
  This package is published without granting an open-source license (`UNLICENSED`).
43
58
 
package/cli.js ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ import { checkProject, writeProject, formatDiagnostic } from './dist/architecture/index.js';
3
+ const args = process.argv.slice(2);
4
+ const command = args.shift();
5
+ if (command === '--help' || command === '-h' || !command) {
6
+ console.log('Usage: hesh check|graph [project-root] [--entry app.hesh] [--tsconfig tsconfig.json] [--write]');
7
+ process.exit(command ? 0 : 1);
8
+ }
9
+ if (!['check', 'graph'].includes(command)) { console.error(`Unknown command ${command}`); process.exit(1); }
10
+ const options = { root: process.cwd() }; let write = false;
11
+ if (args[0] && !args[0].startsWith('--')) options.root = args.shift();
12
+ while (args.length) {
13
+ const flag = args.shift();
14
+ if (flag === '--write') { write = true; continue; }
15
+ if (!['--entry', '--tsconfig'].includes(flag) || !args[0] || args[0].startsWith('--')) { console.error(`Invalid option ${flag}`); process.exit(1); }
16
+ options[flag.slice(2)] = args.shift();
17
+ }
18
+ try {
19
+ const result = checkProject(options);
20
+ for (const item of result.diagnostics) console.error(formatDiagnostic(item));
21
+ if (result.diagnostics.some(item => item.severity === 'error')) process.exitCode = 1;
22
+ else {
23
+ if (write) writeProject(result);
24
+ console.log(command === 'graph' ? JSON.stringify(result.graph, null, 2) : `Architecture valid: ${result.graph.domains.length} domains, ${result.graph.destinations.length} destinations${write ? '; generated JS/declarations written' : ''}.`);
25
+ }
26
+ } catch (error) { console.error(error instanceof Error ? error.message : String(error)); process.exitCode = 1; }
@@ -0,0 +1,12 @@
1
+ export interface ArchitectureDiagnostic {
2
+ code: string;
3
+ severity: 'error' | 'warning';
4
+ message: string;
5
+ file: string;
6
+ offset: number;
7
+ line: number;
8
+ column: number;
9
+ suggestion: string;
10
+ }
11
+ export declare function diagnostic(code: string, message: string, file: string, source: string, offset: number, suggestion: string, severity?: 'error' | 'warning'): ArchitectureDiagnostic;
12
+ export declare function formatDiagnostic(item: ArchitectureDiagnostic): string;
@@ -0,0 +1,8 @@
1
+ export function diagnostic(code, message, file, source, offset, suggestion, severity = 'error') {
2
+ const lines = source.slice(0, offset).split('\n');
3
+ return { code, message, file, offset, line: lines.length, column: lines.at(-1).length + 1, suggestion, severity };
4
+ }
5
+ export function formatDiagnostic(item) {
6
+ return `${item.file}:${item.line}:${item.column} ${item.code} ${item.severity}: ${item.message}\n ${item.suggestion}`;
7
+ }
8
+ //# sourceMappingURL=diagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/architecture/diagnostics.ts"],"names":[],"mappings":"AAIA,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,OAAe,EAAE,IAAY,EAAE,MAAc,EAAE,MAAc,EAAE,UAAkB,EAAE,WAAgC,OAAO;IACjK,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,MAAM,GAAG,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;AACrH,CAAC;AACD,MAAM,UAAU,gBAAgB,CAAC,IAA4B;IAC3D,OAAO,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,OAAO,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC;AACzH,CAAC","sourcesContent":["export interface ArchitectureDiagnostic {\n code: string; severity: 'error' | 'warning'; message: string;\n file: string; offset: number; line: number; column: number; suggestion: string;\n}\nexport function diagnostic(code: string, message: string, file: string, source: string, offset: number, suggestion: string, severity: 'error' | 'warning' = 'error'): ArchitectureDiagnostic {\n const lines = source.slice(0, offset).split('\\n');\n return { code, message, file, offset, line: lines.length, column: lines.at(-1)!.length + 1, suggestion, severity };\n}\nexport function formatDiagnostic(item: ArchitectureDiagnostic): string {\n return `${item.file}:${item.line}:${item.column} ${item.code} ${item.severity}: ${item.message}\\n ${item.suggestion}`;\n}\n"]}
@@ -0,0 +1,16 @@
1
+ import { type ArchitectureDiagnostic } from './diagnostics.js';
2
+ export interface HTMLContract {
3
+ filename: string;
4
+ bindings: string[];
5
+ actions: string[];
6
+ destinations: Record<string, Record<string, string>>;
7
+ validateBinding?: (path: string, offset: number, expected?: string, writable?: boolean) => void;
8
+ }
9
+ export interface HTMLResult {
10
+ code: string;
11
+ diagnostics: ArchitectureDiagnostic[];
12
+ usedBindings: string[];
13
+ usedActions: string[];
14
+ destinations: string[];
15
+ }
16
+ export declare function compileHTML(source: string, contract: HTMLContract): HTMLResult;
@@ -0,0 +1,132 @@
1
+ import { parseFragment } from 'parse5';
2
+ import { diagnostic } from './diagnostics.js';
3
+ export function compileHTML(source, contract) {
4
+ const diagnostics = [], usedBindings = new Set(), usedActions = new Set();
5
+ const report = (code, message, at, suggestion, severity = 'error') => diagnostics.push(diagnostic(code, message, contract.filename, source, at, suggestion, severity));
6
+ const fragment = parseFragment(source, { sourceCodeLocationInfo: true, onParseError: error => report('HESH-HTML-001', error.code, error.startOffset, 'Correct the HTML syntax.') });
7
+ const lines = ["import * as p from '@heshammesalam/heshjs/presentation';", 'export default function render(parent, state, destinations) {'];
8
+ let serial = 0;
9
+ const linked = new Set();
10
+ const path = (value, offset, expected, writable = false) => {
11
+ const pieces = value.trim().split('.');
12
+ if (pieces.some(piece => !/^[$_\p{ID_Start}][$_\u200c\u200d\p{ID_Continue}]*$/u.test(piece) || ['constructor', '__proto__', 'prototype'].includes(piece)))
13
+ report('HESH-BIND-001', `Invalid binding path ${value}`, offset, 'Use a declared dotted data path, not an expression.');
14
+ if (!contract.bindings.includes(pieces[0]))
15
+ report('HESH-BIND-002', `Missing data binding ${pieces[0]}`, offset, 'Declare the binding and return it from the presentation factory.');
16
+ usedBindings.add(pieces[0]);
17
+ contract.validateBinding?.(value.trim(), offset, expected, writable);
18
+ return `p.readBinding(state.bindings, ${JSON.stringify(value.trim())})`;
19
+ };
20
+ const interpolation = (value, offset) => {
21
+ const parts = [];
22
+ let literal = '', cursor = 0;
23
+ const emit = () => { if (literal) {
24
+ parts.push(JSON.stringify(literal));
25
+ literal = '';
26
+ } };
27
+ while (cursor < value.length) {
28
+ if (value[cursor] === '\\' && ['{', '}'].includes(value[cursor + 1] ?? '')) {
29
+ literal += value[cursor + 1];
30
+ cursor += 2;
31
+ continue;
32
+ }
33
+ if (value[cursor] !== '{') {
34
+ literal += value[cursor++];
35
+ continue;
36
+ }
37
+ emit();
38
+ const start = ++cursor, end = value.indexOf('}', start);
39
+ if (end < 0) {
40
+ report('HESH-BIND-001', 'Unterminated interpolation', offset + start - 1, 'Close the binding with }.');
41
+ break;
42
+ }
43
+ parts.push(`String(${path(value.slice(start, end), offset + start)} ?? '')`);
44
+ cursor = end + 1;
45
+ }
46
+ emit();
47
+ return parts.join(' + ') || "''";
48
+ };
49
+ const visit = (node, parent) => {
50
+ const at = node.sourceCodeLocation?.startOffset ?? 0;
51
+ if (node.nodeName === '#text') {
52
+ lines.push(`p.text(${parent}, () => ${interpolation(node.value, at)});`);
53
+ return;
54
+ }
55
+ if (!('tagName' in node))
56
+ return;
57
+ if (node.namespaceURI !== 'http://www.w3.org/1999/xhtml' || ['script', 'iframe', 'object', 'embed', 'base', 'meta', 'link', 'style', 'template'].includes(node.tagName)) {
58
+ report('HESH-HTML-002', `Unsupported active/foreign template element <${node.tagName}>`, at, 'Use semantic HTML and declared CSS/JS modules.');
59
+ return;
60
+ }
61
+ const id = `node${serial++}`;
62
+ lines.push(`const ${id} = p.element(${parent}, ${JSON.stringify(node.tagName)});`);
63
+ const attributes = Object.fromEntries(node.attrs.map(attr => [attr.name, attr.value]));
64
+ const attrAt = (name) => node.sourceCodeLocation?.attrs?.[name]?.startOffset ?? at;
65
+ for (const attr of node.attrs) {
66
+ if (attr.name.startsWith('h-'))
67
+ continue;
68
+ if (/^on/i.test(attr.name) || ['srcdoc', 'innerhtml', 'outerhtml'].includes(attr.name)) {
69
+ report('HESH-HTML-003', `Unsafe attribute ${attr.name}`, attrAt(attr.name), 'Use h-action and safe DOM bindings.');
70
+ continue;
71
+ }
72
+ const wholeBinding = attr.value.startsWith('{') && attr.value.endsWith('}') && !attr.value.slice(1, -1).includes('{') && !attr.value.slice(1, -1).includes('}');
73
+ lines.push(`p.attribute(${id}, ${JSON.stringify(attr.name)}, () => ${wholeBinding ? path(attr.value.slice(1, -1), attrAt(attr.name) + 1) : interpolation(attr.value, attrAt(attr.name))});`);
74
+ }
75
+ for (const name of Object.keys(attributes))
76
+ if (name.startsWith('h-') && !['h-action', 'h-bind', 'h-value', 'h-destination'].includes(name) && !name.startsWith('h-input-'))
77
+ report('HESH-HTML-004', `Unsupported directive ${name}`, attrAt(name), 'Use supported directives; keyed lists/scoped templates are a later milestone.');
78
+ if (attributes['h-bind'] !== undefined) {
79
+ if (node.childNodes.length)
80
+ report('HESH-BIND-003', 'h-bind cannot also contain children', at, 'Remove the children or use text interpolation.');
81
+ lines.push(`p.text(${id}, () => ${path(attributes['h-bind'], attrAt('h-bind'))});`);
82
+ }
83
+ else
84
+ node.childNodes.forEach(child => visit(child, id));
85
+ if (attributes['h-action'] !== undefined) {
86
+ const name = attributes['h-action'];
87
+ usedActions.add(name);
88
+ if (!contract.actions.includes(name))
89
+ report('HESH-ACTION-001', `Missing action ${name}`, attrAt('h-action'), 'Declare and implement the action.');
90
+ lines.push(`p.bindAction(${id}, state, ${JSON.stringify(name)});`);
91
+ }
92
+ if (attributes['h-value'] !== undefined) {
93
+ const name = attributes['h-value'];
94
+ if (!['input', 'textarea', 'select'].includes(node.tagName) || name.includes('.') || ['checkbox', 'radio', 'file'].includes(attributes.type ?? '') || attributes.multiple !== undefined)
95
+ report('HESH-BIND-004', 'h-value requires a single-value text control and root writable signal', attrAt('h-value'), 'Use a text input/textarea/single select and a declared signal.');
96
+ path(name, attrAt('h-value'), undefined, true);
97
+ lines.push(`p.bindValue(${id}, state.bindings, ${JSON.stringify(name)});`);
98
+ }
99
+ const destination = attributes['h-destination'];
100
+ const inputs = Object.keys(attributes).filter(name => name.startsWith('h-input-'));
101
+ if (destination !== undefined) {
102
+ linked.add(destination);
103
+ const schema = contract.destinations[destination];
104
+ if (!schema)
105
+ report('HESH-NAV-001', `Unknown destination ${destination}`, attrAt('h-destination'), 'Declare this destination in the application.');
106
+ if (node.tagName !== 'a' || attributes.href !== undefined)
107
+ report('HESH-NAV-002', 'h-destination requires an anchor without an explicit href', at, 'Use <a h-destination="Name" ...>.');
108
+ for (const key of Object.keys(schema ?? {}))
109
+ if (attributes[`h-input-${key.toLowerCase()}`] === undefined)
110
+ report('HESH-NAV-003', `Missing destination input ${key}`, at, `Add h-input-${key} bound to a declared value.`);
111
+ const entries = inputs.map(attr => {
112
+ const key = Object.keys(schema ?? {}).find(key => key.toLowerCase() === attr.slice(8)) ?? attr.slice(8);
113
+ if (!schema || !Object.hasOwn(schema, key))
114
+ report('HESH-NAV-003', `Unknown destination input ${key}`, attrAt(attr), 'Use only inputs declared by the destination.');
115
+ return `${JSON.stringify(key)}: ${path(attributes[attr], attrAt(attr), schema?.[key])}`;
116
+ });
117
+ lines.push(`p.attribute(${id}, 'href', () => destinations[${JSON.stringify(destination)}].href({${entries.join(',')}}));`);
118
+ }
119
+ else if (inputs.length)
120
+ report('HESH-NAV-003', 'h-input-* requires h-destination', at, 'Declare the destination on this anchor.');
121
+ };
122
+ fragment.childNodes.forEach(node => visit(node, 'parent'));
123
+ lines.push('}');
124
+ for (const name of contract.bindings)
125
+ if (!usedBindings.has(name))
126
+ report('HESH-BIND-005', `Unused binding ${name}`, 0, 'Remove the declaration if it is not needed by this template.', 'warning');
127
+ for (const name of contract.actions)
128
+ if (!usedActions.has(name))
129
+ report('HESH-ACTION-002', `Unused action ${name}`, 0, 'Remove the declaration if it is not needed by this template.', 'warning');
130
+ return { code: lines.join('\n'), diagnostics, usedBindings: [...usedBindings], usedActions: [...usedActions], destinations: [...linked] };
131
+ }
132
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/architecture/html.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseFragment, type DefaultTreeAdapterMap } from 'parse5';\nimport { diagnostic, type ArchitectureDiagnostic } from './diagnostics.js';\nexport interface HTMLContract {\n filename: string; bindings: string[]; actions: string[];\n destinations: Record<string, Record<string, string>>;\n validateBinding?: (path: string, offset: number, expected?: string, writable?: boolean) => void;\n}\nexport interface HTMLResult { code: string; diagnostics: ArchitectureDiagnostic[]; usedBindings: string[]; usedActions: string[]; destinations: string[] }\nexport function compileHTML(source: string, contract: HTMLContract): HTMLResult {\n const diagnostics: ArchitectureDiagnostic[] = [], usedBindings = new Set<string>(), usedActions = new Set<string>();\n const report = (code: string, message: string, at: number, suggestion: string, severity: 'error' | 'warning' = 'error') => diagnostics.push(diagnostic(code, message, contract.filename, source, at, suggestion, severity));\n const fragment = parseFragment(source, { sourceCodeLocationInfo: true, onParseError: error => report('HESH-HTML-001', error.code, error.startOffset, 'Correct the HTML syntax.') });\n const lines = [\"import * as p from '@heshammesalam/heshjs/presentation';\", 'export default function render(parent, state, destinations) {'];\n let serial = 0;\n const linked = new Set<string>();\n const path = (value: string, offset: number, expected?: string, writable = false): string => {\n const pieces = value.trim().split('.');\n if (pieces.some(piece => !/^[$_\\p{ID_Start}][$_\\u200c\\u200d\\p{ID_Continue}]*$/u.test(piece) || ['constructor', '__proto__', 'prototype'].includes(piece))) report('HESH-BIND-001', `Invalid binding path ${value}`, offset, 'Use a declared dotted data path, not an expression.');\n if (!contract.bindings.includes(pieces[0]!)) report('HESH-BIND-002', `Missing data binding ${pieces[0]}`, offset, 'Declare the binding and return it from the presentation factory.');\n usedBindings.add(pieces[0]!); contract.validateBinding?.(value.trim(), offset, expected, writable);\n return `p.readBinding(state.bindings, ${JSON.stringify(value.trim())})`;\n };\n const interpolation = (value: string, offset: number): string => {\n const parts: string[] = []; let literal = '', cursor = 0;\n const emit = () => { if (literal) { parts.push(JSON.stringify(literal)); literal = ''; } };\n while (cursor < value.length) {\n if (value[cursor] === '\\\\' && ['{', '}'].includes(value[cursor + 1] ?? '')) { literal += value[cursor + 1]; cursor += 2; continue; }\n if (value[cursor] !== '{') { literal += value[cursor++]; continue; }\n emit(); const start = ++cursor, end = value.indexOf('}', start);\n if (end < 0) { report('HESH-BIND-001', 'Unterminated interpolation', offset + start - 1, 'Close the binding with }.'); break; }\n parts.push(`String(${path(value.slice(start, end), offset + start)} ?? '')`); cursor = end + 1;\n }\n emit(); return parts.join(' + ') || \"''\";\n };\n const visit = (node: DefaultTreeAdapterMap['childNode'], parent: string): void => {\n const at = node.sourceCodeLocation?.startOffset ?? 0;\n if (node.nodeName === '#text') { lines.push(`p.text(${parent}, () => ${interpolation((node as DefaultTreeAdapterMap['textNode']).value, at)});`); return; }\n if (!('tagName' in node)) return;\n if (node.namespaceURI !== 'http://www.w3.org/1999/xhtml' || ['script', 'iframe', 'object', 'embed', 'base', 'meta', 'link', 'style', 'template'].includes(node.tagName)) {\n report('HESH-HTML-002', `Unsupported active/foreign template element <${node.tagName}>`, at, 'Use semantic HTML and declared CSS/JS modules.'); return;\n }\n const id = `node${serial++}`;\n lines.push(`const ${id} = p.element(${parent}, ${JSON.stringify(node.tagName)});`);\n const attributes = Object.fromEntries(node.attrs.map(attr => [attr.name, attr.value]));\n const attrAt = (name: string) => node.sourceCodeLocation?.attrs?.[name]?.startOffset ?? at;\n for (const attr of node.attrs) {\n if (attr.name.startsWith('h-')) continue;\n if (/^on/i.test(attr.name) || ['srcdoc', 'innerhtml', 'outerhtml'].includes(attr.name)) { report('HESH-HTML-003', `Unsafe attribute ${attr.name}`, attrAt(attr.name), 'Use h-action and safe DOM bindings.'); continue; }\n const wholeBinding = attr.value.startsWith('{') && attr.value.endsWith('}') && !attr.value.slice(1, -1).includes('{') && !attr.value.slice(1, -1).includes('}');\n lines.push(`p.attribute(${id}, ${JSON.stringify(attr.name)}, () => ${wholeBinding ? path(attr.value.slice(1, -1), attrAt(attr.name) + 1) : interpolation(attr.value, attrAt(attr.name))});`);\n }\n for (const name of Object.keys(attributes)) if (name.startsWith('h-') && !['h-action', 'h-bind', 'h-value', 'h-destination'].includes(name) && !name.startsWith('h-input-')) report('HESH-HTML-004', `Unsupported directive ${name}`, attrAt(name), 'Use supported directives; keyed lists/scoped templates are a later milestone.');\n if (attributes['h-bind'] !== undefined) {\n if (node.childNodes.length) report('HESH-BIND-003', 'h-bind cannot also contain children', at, 'Remove the children or use text interpolation.');\n lines.push(`p.text(${id}, () => ${path(attributes['h-bind'], attrAt('h-bind'))});`);\n } else node.childNodes.forEach(child => visit(child, id));\n if (attributes['h-action'] !== undefined) {\n const name = attributes['h-action']; usedActions.add(name);\n if (!contract.actions.includes(name)) report('HESH-ACTION-001', `Missing action ${name}`, attrAt('h-action'), 'Declare and implement the action.');\n lines.push(`p.bindAction(${id}, state, ${JSON.stringify(name)});`);\n }\n if (attributes['h-value'] !== undefined) {\n const name = attributes['h-value'];\n if (!['input', 'textarea', 'select'].includes(node.tagName) || name.includes('.') || ['checkbox', 'radio', 'file'].includes(attributes.type ?? '') || attributes.multiple !== undefined) report('HESH-BIND-004', 'h-value requires a single-value text control and root writable signal', attrAt('h-value'), 'Use a text input/textarea/single select and a declared signal.');\n path(name, attrAt('h-value'), undefined, true);\n lines.push(`p.bindValue(${id}, state.bindings, ${JSON.stringify(name)});`);\n }\n const destination = attributes['h-destination'];\n const inputs = Object.keys(attributes).filter(name => name.startsWith('h-input-'));\n if (destination !== undefined) {\n linked.add(destination);\n const schema = contract.destinations[destination];\n if (!schema) report('HESH-NAV-001', `Unknown destination ${destination}`, attrAt('h-destination'), 'Declare this destination in the application.');\n if (node.tagName !== 'a' || attributes.href !== undefined) report('HESH-NAV-002', 'h-destination requires an anchor without an explicit href', at, 'Use <a h-destination=\"Name\" ...>.');\n for (const key of Object.keys(schema ?? {})) if (attributes[`h-input-${key.toLowerCase()}`] === undefined) report('HESH-NAV-003', `Missing destination input ${key}`, at, `Add h-input-${key} bound to a declared value.`);\n const entries = inputs.map(attr => {\n const key = Object.keys(schema ?? {}).find(key => key.toLowerCase() === attr.slice(8)) ?? attr.slice(8);\n if (!schema || !Object.hasOwn(schema, key)) report('HESH-NAV-003', `Unknown destination input ${key}`, attrAt(attr), 'Use only inputs declared by the destination.');\n return `${JSON.stringify(key)}: ${path(attributes[attr]!, attrAt(attr), schema?.[key])}`;\n });\n lines.push(`p.attribute(${id}, 'href', () => destinations[${JSON.stringify(destination)}].href({${entries.join(',')}}));`);\n } else if (inputs.length) report('HESH-NAV-003', 'h-input-* requires h-destination', at, 'Declare the destination on this anchor.');\n };\n fragment.childNodes.forEach(node => visit(node, 'parent')); lines.push('}');\n for (const name of contract.bindings) if (!usedBindings.has(name)) report('HESH-BIND-005', `Unused binding ${name}`, 0, 'Remove the declaration if it is not needed by this template.', 'warning');\n for (const name of contract.actions) if (!usedActions.has(name)) report('HESH-ACTION-002', `Unused action ${name}`, 0, 'Remove the declaration if it is not needed by this template.', 'warning');\n return { code: lines.join('\\n'), diagnostics, usedBindings: [...usedBindings], usedActions: [...usedActions], destinations: [...linked] };\n}\n"]}
@@ -0,0 +1,42 @@
1
+ import { compileHTML } from './html.js';
2
+ import { formatDiagnostic, type ArchitectureDiagnostic } from './diagnostics.js';
3
+ export { compileHTML, formatDiagnostic };
4
+ export type { ArchitectureDiagnostic };
5
+ export interface ProjectOptions {
6
+ root: string;
7
+ entry?: string;
8
+ tsconfig?: string;
9
+ outDir?: string;
10
+ }
11
+ export interface ProjectResult {
12
+ diagnostics: ArchitectureDiagnostic[];
13
+ files: string[];
14
+ outputs: Record<string, string>;
15
+ graph: {
16
+ domains: {
17
+ name: string;
18
+ exposes: string[];
19
+ consumes: string[];
20
+ }[];
21
+ destinations: {
22
+ name: string;
23
+ url: string;
24
+ input: Record<string, string>;
25
+ owner: string;
26
+ }[];
27
+ links: {
28
+ from: string;
29
+ to: string;
30
+ }[];
31
+ };
32
+ statistics: {
33
+ parsed: number;
34
+ reused: number;
35
+ };
36
+ }
37
+ export declare function createArchitectureCompiler(): {
38
+ clear(): void;
39
+ check(options: ProjectOptions): ProjectResult;
40
+ };
41
+ export declare function checkProject(options: ProjectOptions): ProjectResult;
42
+ export declare function writeProject(result: ProjectResult): void;