@hymarkx/compiler 0.0.2

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 (88) hide show
  1. package/LICENSE-APACHE +202 -0
  2. package/LICENSE-MIT +21 -0
  3. package/README.md +23 -0
  4. package/dist/analyze/index.d.ts +75 -0
  5. package/dist/analyze/index.d.ts.map +1 -0
  6. package/dist/analyze/index.js +455 -0
  7. package/dist/analyze/index.js.map +1 -0
  8. package/dist/components/authored.d.ts +30 -0
  9. package/dist/components/authored.d.ts.map +1 -0
  10. package/dist/components/authored.js +293 -0
  11. package/dist/components/authored.js.map +1 -0
  12. package/dist/components/builtins.d.ts +6 -0
  13. package/dist/components/builtins.d.ts.map +1 -0
  14. package/dist/components/builtins.js +205 -0
  15. package/dist/components/builtins.js.map +1 -0
  16. package/dist/components/styles.d.ts +3 -0
  17. package/dist/components/styles.d.ts.map +1 -0
  18. package/dist/components/styles.js +99 -0
  19. package/dist/components/styles.js.map +1 -0
  20. package/dist/components/types.d.ts +77 -0
  21. package/dist/components/types.d.ts.map +1 -0
  22. package/dist/components/types.js +2 -0
  23. package/dist/components/types.js.map +1 -0
  24. package/dist/components/validate.d.ts +14 -0
  25. package/dist/components/validate.d.ts.map +1 -0
  26. package/dist/components/validate.js +347 -0
  27. package/dist/components/validate.js.map +1 -0
  28. package/dist/diagnostic-origin.d.ts +11 -0
  29. package/dist/diagnostic-origin.d.ts.map +1 -0
  30. package/dist/diagnostic-origin.js +12 -0
  31. package/dist/diagnostic-origin.js.map +1 -0
  32. package/dist/diagnostics/render.d.ts +13 -0
  33. package/dist/diagnostics/render.d.ts.map +1 -0
  34. package/dist/diagnostics/render.js +92 -0
  35. package/dist/diagnostics/render.js.map +1 -0
  36. package/dist/emit/backend.d.ts +13 -0
  37. package/dist/emit/backend.d.ts.map +1 -0
  38. package/dist/emit/backend.js +2 -0
  39. package/dist/emit/backend.js.map +1 -0
  40. package/dist/emit/escape.d.ts +5 -0
  41. package/dist/emit/escape.d.ts.map +1 -0
  42. package/dist/emit/escape.js +52 -0
  43. package/dist/emit/escape.js.map +1 -0
  44. package/dist/emit/html.d.ts +16 -0
  45. package/dist/emit/html.d.ts.map +1 -0
  46. package/dist/emit/html.js +528 -0
  47. package/dist/emit/html.js.map +1 -0
  48. package/dist/emit/sanitize.d.ts +19 -0
  49. package/dist/emit/sanitize.d.ts.map +1 -0
  50. package/dist/emit/sanitize.js +383 -0
  51. package/dist/emit/sanitize.js.map +1 -0
  52. package/dist/events.d.ts +7 -0
  53. package/dist/events.d.ts.map +1 -0
  54. package/dist/events.js +16 -0
  55. package/dist/events.js.map +1 -0
  56. package/dist/expression.d.ts +133 -0
  57. package/dist/expression.d.ts.map +1 -0
  58. package/dist/expression.js +1248 -0
  59. package/dist/expression.js.map +1 -0
  60. package/dist/frontmatter.d.ts +15 -0
  61. package/dist/frontmatter.d.ts.map +1 -0
  62. package/dist/frontmatter.js +196 -0
  63. package/dist/frontmatter.js.map +1 -0
  64. package/dist/index.d.ts +18 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +92 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/islands.d.ts +23 -0
  69. package/dist/islands.d.ts.map +1 -0
  70. package/dist/islands.js +76 -0
  71. package/dist/islands.js.map +1 -0
  72. package/dist/runtime.d.ts +14 -0
  73. package/dist/runtime.d.ts.map +1 -0
  74. package/dist/runtime.js +487 -0
  75. package/dist/runtime.js.map +1 -0
  76. package/dist/styles.d.ts +33 -0
  77. package/dist/styles.d.ts.map +1 -0
  78. package/dist/styles.js +370 -0
  79. package/dist/styles.js.map +1 -0
  80. package/dist/suggestions.d.ts +3 -0
  81. package/dist/suggestions.d.ts.map +1 -0
  82. package/dist/suggestions.js +29 -0
  83. package/dist/suggestions.js.map +1 -0
  84. package/dist/types.d.ts +59 -0
  85. package/dist/types.d.ts.map +1 -0
  86. package/dist/types.js +2 -0
  87. package/dist/types.js.map +1 -0
  88. package/package.json +39 -0
@@ -0,0 +1,13 @@
1
+ import type { Diagnostic } from '@hymarkx/ast';
2
+ import type { AnalyzedDocument } from '../analyze/index.js';
3
+ /** Output returned by a compiler backend. */
4
+ export interface EmitResult {
5
+ readonly html: string;
6
+ readonly diagnostics: readonly Diagnostic[];
7
+ }
8
+ /** Backend boundary kept stable so a future IR can be inserted without emitter rewrites. */
9
+ export interface Backend<TOptions = unknown> {
10
+ readonly name: string;
11
+ emit(document: AnalyzedDocument, options: TOptions): EmitResult;
12
+ }
13
+ //# sourceMappingURL=backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/emit/backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAE3D,6CAA6C;AAC7C,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,EAAE,SAAS,UAAU,EAAE,CAAA;CAC5C;AAED,4FAA4F;AAC5F,MAAM,WAAW,OAAO,CAAC,QAAQ,GAAG,OAAO;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,IAAI,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,EAAE,QAAQ,GAAG,UAAU,CAAA;CAChE"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend.js","sourceRoot":"","sources":["../../src/emit/backend.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ /** Escapes text for HTML text and quoted-attribute positions. */
2
+ export declare function escapeHtml(value: string): string;
3
+ /** Percent-encodes a URL using the byte-level set used by the CommonMark reference output. */
4
+ export declare function encodeUrl(value: string): string;
5
+ //# sourceMappingURL=escape.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escape.d.ts","sourceRoot":"","sources":["../../src/emit/escape.ts"],"names":[],"mappings":"AAEA,iEAAiE;AACjE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMhD;AA6BD,8FAA8F;AAC9F,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAkB/C"}
@@ -0,0 +1,52 @@
1
+ const URL_SAFE_ASCII = /^[A-Za-z0-9\-_.~!*'();:@&=+$,/?#%]$/;
2
+ /** Escapes text for HTML text and quoted-attribute positions. */
3
+ export function escapeHtml(value) {
4
+ return value
5
+ .replaceAll('&', '&amp;')
6
+ .replaceAll('<', '&lt;')
7
+ .replaceAll('>', '&gt;')
8
+ .replaceAll('"', '&quot;');
9
+ }
10
+ function percentByte(value) {
11
+ return `%${value.toString(16).toUpperCase().padStart(2, '0')}`;
12
+ }
13
+ function encodeCodePoint(value) {
14
+ const codePoint = value >= 0xd800 && value <= 0xdfff ? 0xfffd : value;
15
+ if (codePoint <= 0x7f) {
16
+ return percentByte(codePoint);
17
+ }
18
+ if (codePoint <= 0x7ff) {
19
+ return percentByte(0xc0 | (codePoint >> 6)) + percentByte(0x80 | (codePoint & 0x3f));
20
+ }
21
+ if (codePoint <= 0xffff) {
22
+ return (percentByte(0xe0 | (codePoint >> 12)) +
23
+ percentByte(0x80 | ((codePoint >> 6) & 0x3f)) +
24
+ percentByte(0x80 | (codePoint & 0x3f)));
25
+ }
26
+ return (percentByte(0xf0 | (codePoint >> 18)) +
27
+ percentByte(0x80 | ((codePoint >> 12) & 0x3f)) +
28
+ percentByte(0x80 | ((codePoint >> 6) & 0x3f)) +
29
+ percentByte(0x80 | (codePoint & 0x3f)));
30
+ }
31
+ /** Percent-encodes a URL using the byte-level set used by the CommonMark reference output. */
32
+ export function encodeUrl(value) {
33
+ let output = '';
34
+ for (let index = 0; index < value.length; index += 1) {
35
+ const codePoint = value.codePointAt(index);
36
+ if (codePoint === undefined) {
37
+ continue;
38
+ }
39
+ const character = String.fromCodePoint(codePoint);
40
+ if (codePoint <= 0x7f && URL_SAFE_ASCII.test(character)) {
41
+ output += character;
42
+ }
43
+ else {
44
+ output += encodeCodePoint(codePoint);
45
+ }
46
+ if (codePoint > 0xffff) {
47
+ index += 1;
48
+ }
49
+ }
50
+ return output;
51
+ }
52
+ //# sourceMappingURL=escape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escape.js","sourceRoot":"","sources":["../../src/emit/escape.ts"],"names":[],"mappings":"AAAA,MAAM,cAAc,GAAG,qCAAqC,CAAA;AAE5D,iEAAiE;AACjE,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,KAAK;SACT,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;SACxB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAA;AAC9B,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,OAAO,IAAI,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAA;AAChE,CAAC;AAED,SAAS,eAAe,CAAC,KAAa;IACpC,MAAM,SAAS,GAAG,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAA;IACrE,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;QACtB,OAAO,WAAW,CAAC,SAAS,CAAC,CAAA;IAC/B,CAAC;IACD,IAAI,SAAS,IAAI,KAAK,EAAE,CAAC;QACvB,OAAO,WAAW,CAAC,IAAI,GAAG,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,IAAI,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,CAAA;IACtF,CAAC;IACD,IAAI,SAAS,IAAI,MAAM,EAAE,CAAC;QACxB,OAAO,CACL,WAAW,CAAC,IAAI,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;YACrC,WAAW,CAAC,IAAI,GAAG,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;YAC7C,WAAW,CAAC,IAAI,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,CACvC,CAAA;IACH,CAAC;IACD,OAAO,CACL,WAAW,CAAC,IAAI,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;QACrC,WAAW,CAAC,IAAI,GAAG,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QAC9C,WAAW,CAAC,IAAI,GAAG,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC7C,WAAW,CAAC,IAAI,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,CACvC,CAAA;AACH,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,IAAI,MAAM,GAAG,EAAE,CAAA;IACf,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACrD,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;QAC1C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,SAAQ;QACV,CAAC;QACD,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAA;QACjD,IAAI,SAAS,IAAI,IAAI,IAAI,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,SAAS,CAAA;QACrB,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,eAAe,CAAC,SAAS,CAAC,CAAA;QACtC,CAAC;QACD,IAAI,SAAS,GAAG,MAAM,EAAE,CAAC;YACvB,KAAK,IAAI,CAAC,CAAA;QACZ,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { Html } from '@hymarkx/ast';
2
+ import type { AnalyzedDocument } from '../analyze/index.js';
3
+ import type { TrustMode } from '../types.js';
4
+ import type { InteractivityPlan } from '../runtime.js';
5
+ import type { Backend } from './backend.js';
6
+ interface HtmlOptions {
7
+ readonly trust: TrustMode;
8
+ readonly omittedNodes: ReadonlyMap<AnalyzedDocument, ReadonlySet<Html>>;
9
+ readonly rootScopeAttributes: readonly string[];
10
+ readonly componentScopeAttributes: ReadonlyMap<AnalyzedDocument, readonly string[]>;
11
+ readonly interactivity: InteractivityPlan;
12
+ }
13
+ /** Original HMX-AST-to-HTML backend. */
14
+ export declare const htmlBackend: Backend<HtmlOptions>;
15
+ export {};
16
+ //# sourceMappingURL=html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/emit/html.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA0B,IAAI,EAA+B,MAAM,cAAc,CAAA;AAC7F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAG5C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,KAAK,EAAE,OAAO,EAAc,MAAM,cAAc,CAAA;AAUvD,UAAU,WAAW;IACnB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;IACzB,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,gBAAgB,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAA;IACvE,QAAQ,CAAC,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAA;IAC/C,QAAQ,CAAC,wBAAwB,EAAE,WAAW,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC,CAAA;IACnF,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAA;CAC1C;AAgrBD,wCAAwC;AACxC,eAAO,MAAM,WAAW,EAAE,OAAO,CAAC,WAAW,CAG5C,CAAA"}
@@ -0,0 +1,528 @@
1
+ import { createDiagnostic, visit } from '@hymarkx/ast';
2
+ import { setDiagnosticOrigin } from '../diagnostic-origin.js';
3
+ import { encodeUrl, escapeHtml } from './escape.js';
4
+ import { addAttributesToRawHtml, addUniversalAttributesToFirstElement, componentHtmlDiagnostics, isAllowedDocumentUrl, sanitizeRawHtml, } from './sanitize.js';
5
+ const blockContext = { block: true };
6
+ const inlineContext = { block: false };
7
+ function write(value) {
8
+ return { kind: 'write', value };
9
+ }
10
+ function emitNode(node, context, document) {
11
+ return { kind: 'node', node, context, document };
12
+ }
13
+ function pushInOrder(stack, actions) {
14
+ for (let index = actions.length - 1; index >= 0; index -= 1) {
15
+ const action = actions[index];
16
+ if (action !== undefined) {
17
+ stack.push(action);
18
+ }
19
+ }
20
+ }
21
+ function childActions(nodes, context, document) {
22
+ return nodes.map((node) => emitNode(node, context, document));
23
+ }
24
+ function scopeAttributesFor(document, options) {
25
+ return [...options.rootScopeAttributes, ...(options.componentScopeAttributes.get(document) ?? [])];
26
+ }
27
+ function scopeFor(document, options) {
28
+ return scopeAttributesFor(document, options)
29
+ .map((attribute) => ` ${attribute}`)
30
+ .join('');
31
+ }
32
+ function universalAttributeValues(attributes) {
33
+ return Object.fromEntries(['id', 'class', 'title'].flatMap((name) => {
34
+ const value = attributes[name];
35
+ return typeof value === 'string' ? [[name, value]] : [];
36
+ }));
37
+ }
38
+ function elementStart(element, scope) {
39
+ let output = `<${element.tag}`;
40
+ for (const [name, value] of Object.entries(element.attributes)) {
41
+ output += ` ${name}="${escapeHtml(value)}"`;
42
+ }
43
+ return `${output}${scope}>`;
44
+ }
45
+ function withUniversalAttributes(plan, attributes) {
46
+ const outer = plan.wrappers[0];
47
+ if (outer === undefined) {
48
+ return plan;
49
+ }
50
+ const authorClass = typeof attributes.class === 'string' ? attributes.class : undefined;
51
+ const rendererClass = outer.attributes.class;
52
+ const mergedClass = [rendererClass, authorClass].filter(Boolean).join(' ');
53
+ const outerAttributes = {
54
+ ...outer.attributes,
55
+ ...(mergedClass === '' ? {} : { class: mergedClass }),
56
+ ...(typeof attributes.id === 'string' ? { id: attributes.id } : {}),
57
+ ...(typeof attributes.title === 'string' ? { title: attributes.title } : {}),
58
+ };
59
+ return {
60
+ ...plan,
61
+ wrappers: [{ ...outer, attributes: outerAttributes }, ...plan.wrappers.slice(1)],
62
+ };
63
+ }
64
+ function interactionAttributes(node, document, options) {
65
+ const text = options.interactivity.attributeMarkers.get(document)?.get(node);
66
+ const event = options.interactivity.eventMarkers.get(document)?.get(node);
67
+ const inputValue = options.interactivity.inputValues.get(document)?.get(node);
68
+ return {
69
+ ...(text === undefined ? {} : { 'data-hmx-a': text }),
70
+ ...(event === undefined ? {} : { 'data-hmx-e': event }),
71
+ ...(inputValue === undefined ? {} : { value: inputValue }),
72
+ };
73
+ }
74
+ function withOuterAttributes(plan, attributes) {
75
+ const outer = plan.wrappers[0];
76
+ if (outer === undefined || Object.keys(attributes).length === 0)
77
+ return plan;
78
+ return {
79
+ ...plan,
80
+ wrappers: [
81
+ { ...outer, attributes: { ...outer.attributes, ...attributes } },
82
+ ...plan.wrappers.slice(1),
83
+ ],
84
+ };
85
+ }
86
+ function directiveActions(node, context, document, scope, options) {
87
+ if (node.type === 'leafDirective' && node.name === 'state') {
88
+ return [];
89
+ }
90
+ const island = document.islandNodes.get(node);
91
+ if (island !== undefined) {
92
+ // A placeholder and nothing else. The host bundles the module and mounts into this
93
+ // element; the compiler has deliberately not looked at the file (ADR-0016).
94
+ return [write(`<div data-hmx-island="${island.id}"${scope}></div>${context.block ? '\n' : ''}`)];
95
+ }
96
+ const projection = document.projections.get(node);
97
+ if (projection !== undefined) {
98
+ return childActions(projection.nodes, blockContext, projection.document);
99
+ }
100
+ const expansion = document.expansions.get(node);
101
+ if (expansion !== undefined) {
102
+ const component = document.components.get(node);
103
+ const boundary = {};
104
+ return [
105
+ { kind: 'componentStart', boundary },
106
+ ...childActions(expansion.document.root.children, blockContext, expansion.document),
107
+ {
108
+ kind: 'componentEnd',
109
+ boundary,
110
+ attributes: {
111
+ ...universalAttributeValues(component?.attributes ?? {}),
112
+ ...interactionAttributes(node, document, options),
113
+ },
114
+ scope: scopeFor(expansion.document, options),
115
+ },
116
+ ];
117
+ }
118
+ const component = document.components.get(node);
119
+ if (component === undefined || !component.kindAllowed) {
120
+ if (node.type === 'textDirective') {
121
+ return childActions(node.children, inlineContext, document);
122
+ }
123
+ if (node.type === 'leafDirective') {
124
+ return childActions(node.label ?? [], inlineContext, document);
125
+ }
126
+ return [
127
+ ...childActions(node.label ?? [], inlineContext, document),
128
+ ...childActions(node.children, blockContext, document),
129
+ ];
130
+ }
131
+ const plan = withOuterAttributes(withUniversalAttributes(component.renderer(node, component.attributes), component.attributes), interactionAttributes(node, document, options));
132
+ const actions = plan.wrappers.map((wrapper) => write(elementStart(wrapper, scope)));
133
+ const label = node.type === 'textDirective' ? [] : (node.label ?? []);
134
+ if (label.length > 0) {
135
+ if (plan.labelWrapper !== undefined) {
136
+ actions.push(write(elementStart(plan.labelWrapper, scope)));
137
+ }
138
+ actions.push(...childActions(label, inlineContext, document));
139
+ if (plan.labelWrapper !== undefined) {
140
+ actions.push(write(`</${plan.labelWrapper.tag}>`));
141
+ }
142
+ }
143
+ if (node.type === 'textDirective') {
144
+ actions.push(...childActions(node.children, inlineContext, document));
145
+ }
146
+ else if (node.type === 'containerDirective') {
147
+ const paragraph = plan.flattenSingleParagraph === true ? node.children[0] : undefined;
148
+ if (paragraph?.type === 'paragraph' && node.children.length === 1) {
149
+ actions.push(...childActions(paragraph.children, inlineContext, document));
150
+ }
151
+ else {
152
+ actions.push(...childActions(node.children, blockContext, document));
153
+ }
154
+ }
155
+ for (let index = plan.wrappers.length - 1; index >= 0; index -= 1) {
156
+ const wrapper = plan.wrappers[index];
157
+ if (wrapper !== undefined && wrapper.void !== true) {
158
+ actions.push(write(`</${wrapper.tag}>${context.block && index === 0 ? '\n' : ''}`));
159
+ }
160
+ }
161
+ if (plan.wrappers[0]?.void === true && context.block)
162
+ actions.push(write('\n'));
163
+ return actions;
164
+ }
165
+ function collectDefinitions(root) {
166
+ const definitions = new Map();
167
+ visit(root, (node) => {
168
+ if (node.type === 'definition' && !definitions.has(node.identifier)) {
169
+ definitions.set(node.identifier, node);
170
+ }
171
+ });
172
+ return definitions;
173
+ }
174
+ function taskPrefix(checked, scope) {
175
+ return checked
176
+ ? `<input type="checkbox" disabled="" checked=""${scope} /> `
177
+ : `<input type="checkbox" disabled=""${scope} /> `;
178
+ }
179
+ function listItemActions(node, context, scope, document) {
180
+ const children = node.children.filter((child) => child.type !== 'definition');
181
+ const tight = context.tightList === true && !node.spread;
182
+ const className = node.checked === null ? '' : ' class="task-list-item"';
183
+ const actions = [write(`<li${className}${scope}>`)];
184
+ if (!tight && children.length > 0) {
185
+ actions.push(write('\n'));
186
+ }
187
+ for (let index = 0; index < children.length; index += 1) {
188
+ const child = children[index];
189
+ if (child === undefined) {
190
+ continue;
191
+ }
192
+ if (tight && child.type === 'paragraph') {
193
+ actions.push(emitNode(child, {
194
+ block: true,
195
+ tightParagraph: true,
196
+ ...(index === 0 && node.checked !== null
197
+ ? { taskPrefix: taskPrefix(node.checked, scope) }
198
+ : {}),
199
+ }, document));
200
+ if (index < children.length - 1) {
201
+ actions.push(write('\n'));
202
+ }
203
+ }
204
+ else {
205
+ if (tight && index === 0) {
206
+ actions.push(write('\n'));
207
+ }
208
+ actions.push(emitNode(child, blockContext, document));
209
+ }
210
+ }
211
+ actions.push(write('</li>\n'));
212
+ return actions;
213
+ }
214
+ function tableCellActions(node, context, scope, document) {
215
+ const tag = context.tableHeader === true ? 'th' : 'td';
216
+ const alignment = context.tableAlign === null || context.tableAlign === undefined
217
+ ? ''
218
+ : ` align="${context.tableAlign}"`;
219
+ return [
220
+ write(`<${tag}${alignment}${scope}>`),
221
+ ...childActions(node.children, inlineContext, document),
222
+ write(`</${tag}>\n`),
223
+ ];
224
+ }
225
+ function emptyTableCell(header, align, scope) {
226
+ const tag = header ? 'th' : 'td';
227
+ const alignment = align === null ? '' : ` align="${align}"`;
228
+ return write(`<${tag}${alignment}${scope}></${tag}>\n`);
229
+ }
230
+ function tableRowActions(cells, alignments, header, scope, document) {
231
+ const actions = [write(`<tr${scope}>\n`)];
232
+ for (let index = 0; index < alignments.length; index += 1) {
233
+ const alignment = alignments[index] ?? null;
234
+ const cell = cells[index];
235
+ actions.push(cell === undefined
236
+ ? emptyTableCell(header, alignment, scope)
237
+ : emitNode(cell, { block: true, tableHeader: header, tableAlign: alignment }, document));
238
+ }
239
+ actions.push(write('</tr>\n'));
240
+ return actions;
241
+ }
242
+ function securityDiagnostic(url, span, document) {
243
+ const diagnostic = createDiagnostic({
244
+ code: 'HMX3003',
245
+ severity: 'error',
246
+ message: `URL "${url}" uses a scheme that is not allowed in document mode.`,
247
+ span,
248
+ });
249
+ setDiagnosticOrigin(diagnostic, document.source, document.from);
250
+ return diagnostic;
251
+ }
252
+ function urlAttribute(name, value, span, trust, diagnostics, document) {
253
+ if (trust === 'document' && !isAllowedDocumentUrl(value)) {
254
+ diagnostics.push(securityDiagnostic(value, span, document));
255
+ return '';
256
+ }
257
+ return ` ${name}="${escapeHtml(encodeUrl(value))}"`;
258
+ }
259
+ function assertNeverNode(node) {
260
+ throw new TypeError(`Unhandled node type in HTML emitter: ${JSON.stringify(node)}`);
261
+ }
262
+ function emitHtml(document, options) {
263
+ const definitions = new Map();
264
+ const definitionsFor = (owner) => {
265
+ const existing = definitions.get(owner);
266
+ if (existing !== undefined) {
267
+ return existing;
268
+ }
269
+ const collected = collectDefinitions(owner.root);
270
+ definitions.set(owner, collected);
271
+ return collected;
272
+ };
273
+ const diagnostics = [];
274
+ const chunks = [];
275
+ const stack = [emitNode(document.root, blockContext, document)];
276
+ while (stack.length > 0) {
277
+ const action = stack.pop();
278
+ if (action === undefined) {
279
+ continue;
280
+ }
281
+ if (action.kind === 'write') {
282
+ chunks.push(action.value);
283
+ continue;
284
+ }
285
+ if (action.kind === 'componentStart') {
286
+ action.boundary.start = chunks.length;
287
+ continue;
288
+ }
289
+ if (action.kind === 'componentEnd') {
290
+ const start = action.boundary.start ?? chunks.length;
291
+ const fragment = chunks.splice(start).join('');
292
+ const attributed = addUniversalAttributesToFirstElement(fragment, action.attributes);
293
+ if (attributed !== undefined) {
294
+ chunks.push(attributed);
295
+ }
296
+ else if (Object.keys(action.attributes).length > 0) {
297
+ chunks.push(`${elementStart({ tag: 'div', attributes: action.attributes }, action.scope)}${fragment}</div>\n`);
298
+ }
299
+ else {
300
+ chunks.push(fragment);
301
+ }
302
+ continue;
303
+ }
304
+ const { node, context, document: owner } = action;
305
+ const scope = scopeFor(owner, options);
306
+ switch (node.type) {
307
+ case 'root':
308
+ pushInOrder(stack, childActions(node.children, blockContext, owner));
309
+ break;
310
+ case 'yaml':
311
+ break;
312
+ case 'paragraph': {
313
+ const content = childActions(node.children, inlineContext, owner);
314
+ if (context.tightParagraph === true) {
315
+ pushInOrder(stack, [
316
+ ...(context.taskPrefix === undefined ? [] : [write(context.taskPrefix)]),
317
+ ...content,
318
+ ]);
319
+ }
320
+ else {
321
+ pushInOrder(stack, [write(`<p${scope}>`), ...content, write('</p>\n')]);
322
+ }
323
+ break;
324
+ }
325
+ case 'heading':
326
+ pushInOrder(stack, [
327
+ write(`<h${node.depth}${scope}>`),
328
+ ...childActions(node.children, inlineContext, owner),
329
+ write(`</h${node.depth}>\n`),
330
+ ]);
331
+ break;
332
+ case 'thematicBreak':
333
+ chunks.push(`<hr${scope} />\n`);
334
+ break;
335
+ case 'blockquote':
336
+ pushInOrder(stack, [
337
+ write(`<blockquote${scope}>\n`),
338
+ ...childActions(node.children, blockContext, owner),
339
+ write('</blockquote>\n'),
340
+ ]);
341
+ break;
342
+ case 'list': {
343
+ const taskList = node.children.some((item) => item.checked !== null);
344
+ const tight = !node.spread && node.children.every((item) => !item.spread);
345
+ const className = taskList ? ' class="contains-task-list"' : '';
346
+ const start = node.ordered && node.start !== null && node.start !== 1 ? ` start="${node.start}"` : '';
347
+ const tag = node.ordered ? 'ol' : 'ul';
348
+ pushInOrder(stack, [
349
+ write(`<${tag}${start}${className}${scope}>\n`),
350
+ ...node.children.map((child) => emitNode(child, { block: true, tightList: tight }, owner)),
351
+ write(`</${tag}>\n`),
352
+ ]);
353
+ break;
354
+ }
355
+ case 'listItem':
356
+ pushInOrder(stack, listItemActions(node, context, scope, owner));
357
+ break;
358
+ case 'code': {
359
+ const language = node.lang === null ? '' : ` class="language-${escapeHtml(node.lang)}"`;
360
+ const content = node.value.length === 0 ? '' : `${escapeHtml(node.value)}\n`;
361
+ chunks.push(`<pre${scope}><code${language}${scope}>${content}</code></pre>\n`);
362
+ break;
363
+ }
364
+ case 'html': {
365
+ if (options.omittedNodes.get(owner)?.has(node) === true) {
366
+ break;
367
+ }
368
+ const scopeAttributes = scopeAttributesFor(owner, options);
369
+ if (owner.authored !== undefined &&
370
+ componentHtmlDiagnostics(node.value, node.position).length > 0) {
371
+ break;
372
+ }
373
+ if (options.trust === 'app') {
374
+ const html = addAttributesToRawHtml(node.value, scopeAttributes);
375
+ chunks.push(html);
376
+ if (context.block && html.length > 0 && !html.endsWith('\n')) {
377
+ chunks.push('\n');
378
+ }
379
+ break;
380
+ }
381
+ const sanitized = sanitizeRawHtml(node.value, node.position);
382
+ for (const diagnostic of sanitized.diagnostics) {
383
+ setDiagnosticOrigin(diagnostic, owner.source, owner.from);
384
+ }
385
+ diagnostics.push(...sanitized.diagnostics);
386
+ const html = addAttributesToRawHtml(sanitized.html, scopeAttributes);
387
+ chunks.push(html);
388
+ if (context.block && html.length > 0 && !html.endsWith('\n')) {
389
+ chunks.push('\n');
390
+ }
391
+ break;
392
+ }
393
+ case 'text':
394
+ chunks.push(escapeHtml(node.value));
395
+ break;
396
+ case 'interpolation':
397
+ {
398
+ const value = escapeHtml(owner.interpolations.get(node) ?? '');
399
+ const marker = options.interactivity.textMarkers.get(owner)?.get(node);
400
+ chunks.push(marker === undefined ? value : `<span data-hmx-t="${marker}"${scope}>${value}</span>`);
401
+ }
402
+ break;
403
+ case 'emphasis':
404
+ pushInOrder(stack, [
405
+ write(`<em${scope}>`),
406
+ ...childActions(node.children, inlineContext, owner),
407
+ write('</em>'),
408
+ ]);
409
+ break;
410
+ case 'strong':
411
+ pushInOrder(stack, [
412
+ write(`<strong${scope}>`),
413
+ ...childActions(node.children, inlineContext, owner),
414
+ write('</strong>'),
415
+ ]);
416
+ break;
417
+ case 'delete':
418
+ pushInOrder(stack, [
419
+ write(`<del${scope}>`),
420
+ ...childActions(node.children, inlineContext, owner),
421
+ write('</del>'),
422
+ ]);
423
+ break;
424
+ case 'inlineCode':
425
+ chunks.push(`<code${scope}>${escapeHtml(node.value.replaceAll('\n', ' '))}</code>`);
426
+ break;
427
+ case 'break':
428
+ chunks.push(`<br${scope} />\n`);
429
+ break;
430
+ case 'link': {
431
+ const href = urlAttribute('href', node.url, node.position, owner.authored === undefined ? options.trust : 'document', diagnostics, owner);
432
+ const title = node.title === null ? '' : ` title="${escapeHtml(node.title)}"`;
433
+ pushInOrder(stack, [
434
+ write(`<a${href}${title}${scope}>`),
435
+ ...childActions(node.children, inlineContext, owner),
436
+ write('</a>'),
437
+ ]);
438
+ break;
439
+ }
440
+ case 'image': {
441
+ const src = urlAttribute('src', node.url, node.position, owner.authored === undefined ? options.trust : 'document', diagnostics, owner);
442
+ const alt = escapeHtml(node.alt ?? '');
443
+ const title = node.title === null ? '' : ` title="${escapeHtml(node.title)}"`;
444
+ chunks.push(`<img${src} alt="${alt}"${title}${scope} />`);
445
+ break;
446
+ }
447
+ case 'definition':
448
+ break;
449
+ case 'linkReference': {
450
+ const definition = definitionsFor(owner).get(node.identifier);
451
+ if (definition === undefined) {
452
+ pushInOrder(stack, childActions(node.children, inlineContext, owner));
453
+ break;
454
+ }
455
+ const href = urlAttribute('href', definition.url, definition.position, owner.authored === undefined ? options.trust : 'document', diagnostics, owner);
456
+ const title = definition.title === null ? '' : ` title="${escapeHtml(definition.title)}"`;
457
+ pushInOrder(stack, [
458
+ write(`<a${href}${title}${scope}>`),
459
+ ...childActions(node.children, inlineContext, owner),
460
+ write('</a>'),
461
+ ]);
462
+ break;
463
+ }
464
+ case 'imageReference': {
465
+ const definition = definitionsFor(owner).get(node.identifier);
466
+ if (definition === undefined) {
467
+ chunks.push(escapeHtml(node.alt ?? ''));
468
+ break;
469
+ }
470
+ const src = urlAttribute('src', definition.url, definition.position, owner.authored === undefined ? options.trust : 'document', diagnostics, owner);
471
+ const title = definition.title === null ? '' : ` title="${escapeHtml(definition.title)}"`;
472
+ chunks.push(`<img${src} alt="${escapeHtml(node.alt ?? '')}"${title}${scope} />`);
473
+ break;
474
+ }
475
+ case 'table': {
476
+ const rows = node.children;
477
+ const actions = [write(`<table${scope}>\n`)];
478
+ const header = rows[0];
479
+ if (header !== undefined) {
480
+ actions.push(write(`<thead${scope}>\n`));
481
+ actions.push(...tableRowActions(header.children, node.align, true, scope, owner), write('</thead>\n'));
482
+ }
483
+ if (rows.length > 1) {
484
+ actions.push(write(`<tbody${scope}>\n`));
485
+ for (let rowIndex = 1; rowIndex < rows.length; rowIndex += 1) {
486
+ const row = rows[rowIndex];
487
+ if (row === undefined) {
488
+ continue;
489
+ }
490
+ actions.push(...tableRowActions(row.children, node.align, false, scope, owner));
491
+ }
492
+ actions.push(write('</tbody>\n'));
493
+ }
494
+ actions.push(write('</table>\n'));
495
+ pushInOrder(stack, actions);
496
+ break;
497
+ }
498
+ case 'tableRow':
499
+ pushInOrder(stack, [
500
+ write(`<tr${scope}>\n`),
501
+ ...node.children.map((cell) => emitNode(cell, context, owner)),
502
+ write('</tr>\n'),
503
+ ]);
504
+ break;
505
+ case 'tableCell':
506
+ pushInOrder(stack, tableCellActions(node, context, scope, owner));
507
+ break;
508
+ case 'textDirective':
509
+ pushInOrder(stack, directiveActions(node, context, owner, scope, options));
510
+ break;
511
+ case 'leafDirective':
512
+ pushInOrder(stack, directiveActions(node, context, owner, scope, options));
513
+ break;
514
+ case 'containerDirective':
515
+ pushInOrder(stack, directiveActions(node, context, owner, scope, options));
516
+ break;
517
+ default:
518
+ assertNeverNode(node);
519
+ }
520
+ }
521
+ return { html: chunks.join(''), diagnostics };
522
+ }
523
+ /** Original HMX-AST-to-HTML backend. */
524
+ export const htmlBackend = {
525
+ name: 'html',
526
+ emit: emitHtml,
527
+ };
528
+ //# sourceMappingURL=html.js.map