@opentf/web 0.4.0 → 0.6.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 (187) hide show
  1. package/README.md +30 -28
  2. package/components/Link.jsx +43 -0
  3. package/core/errors.js +48 -0
  4. package/core/reactive.js +190 -0
  5. package/core/signals.js +207 -0
  6. package/index.js +13 -0
  7. package/package.json +27 -18
  8. package/runtime/clipboard.js +47 -0
  9. package/runtime/code-block.js +47 -0
  10. package/runtime/context.js +99 -0
  11. package/runtime/dom.js +368 -0
  12. package/runtime/error-boundary.js +109 -0
  13. package/runtime/events.js +17 -0
  14. package/runtime/hydrate.js +121 -0
  15. package/runtime/index.js +22 -0
  16. package/runtime/lifecycle.js +9 -0
  17. package/runtime/mount.js +49 -0
  18. package/runtime/portal.js +59 -0
  19. package/runtime/raw-html.js +33 -0
  20. package/runtime/router.js +392 -0
  21. package/server/builtins.js +16 -0
  22. package/server/head.js +191 -0
  23. package/server/index.js +8 -0
  24. package/server/render.js +98 -0
  25. package/server/ssg-runtime.js +112 -0
  26. package/dist/CHANGELOG.md +0 -49
  27. package/dist/README.md +0 -41
  28. package/dist/babel.config.cjs +0 -6
  29. package/dist/compiler/babel-plugin.cjs +0 -798
  30. package/dist/core/mount.js +0 -32
  31. package/dist/index.js +0 -7
  32. package/dist/node_modules/@babel/core/LICENSE +0 -22
  33. package/dist/node_modules/@babel/core/README.md +0 -19
  34. package/dist/node_modules/@babel/core/lib/config/cache-contexts.js +0 -5
  35. package/dist/node_modules/@babel/core/lib/config/cache-contexts.js.map +0 -1
  36. package/dist/node_modules/@babel/core/lib/config/caching.js +0 -261
  37. package/dist/node_modules/@babel/core/lib/config/caching.js.map +0 -1
  38. package/dist/node_modules/@babel/core/lib/config/config-chain.js +0 -469
  39. package/dist/node_modules/@babel/core/lib/config/config-chain.js.map +0 -1
  40. package/dist/node_modules/@babel/core/lib/config/config-descriptors.js +0 -190
  41. package/dist/node_modules/@babel/core/lib/config/config-descriptors.js.map +0 -1
  42. package/dist/node_modules/@babel/core/lib/config/files/configuration.js +0 -290
  43. package/dist/node_modules/@babel/core/lib/config/files/configuration.js.map +0 -1
  44. package/dist/node_modules/@babel/core/lib/config/files/import.cjs +0 -6
  45. package/dist/node_modules/@babel/core/lib/config/files/import.cjs.map +0 -1
  46. package/dist/node_modules/@babel/core/lib/config/files/index-browser.js +0 -58
  47. package/dist/node_modules/@babel/core/lib/config/files/index-browser.js.map +0 -1
  48. package/dist/node_modules/@babel/core/lib/config/files/index.js +0 -78
  49. package/dist/node_modules/@babel/core/lib/config/files/index.js.map +0 -1
  50. package/dist/node_modules/@babel/core/lib/config/files/module-types.js +0 -203
  51. package/dist/node_modules/@babel/core/lib/config/files/module-types.js.map +0 -1
  52. package/dist/node_modules/@babel/core/lib/config/files/package.js +0 -61
  53. package/dist/node_modules/@babel/core/lib/config/files/package.js.map +0 -1
  54. package/dist/node_modules/@babel/core/lib/config/files/plugins.js +0 -220
  55. package/dist/node_modules/@babel/core/lib/config/files/plugins.js.map +0 -1
  56. package/dist/node_modules/@babel/core/lib/config/files/types.js +0 -5
  57. package/dist/node_modules/@babel/core/lib/config/files/types.js.map +0 -1
  58. package/dist/node_modules/@babel/core/lib/config/files/utils.js +0 -36
  59. package/dist/node_modules/@babel/core/lib/config/files/utils.js.map +0 -1
  60. package/dist/node_modules/@babel/core/lib/config/full.js +0 -312
  61. package/dist/node_modules/@babel/core/lib/config/full.js.map +0 -1
  62. package/dist/node_modules/@babel/core/lib/config/helpers/config-api.js +0 -85
  63. package/dist/node_modules/@babel/core/lib/config/helpers/config-api.js.map +0 -1
  64. package/dist/node_modules/@babel/core/lib/config/helpers/deep-array.js +0 -23
  65. package/dist/node_modules/@babel/core/lib/config/helpers/deep-array.js.map +0 -1
  66. package/dist/node_modules/@babel/core/lib/config/helpers/environment.js +0 -12
  67. package/dist/node_modules/@babel/core/lib/config/helpers/environment.js.map +0 -1
  68. package/dist/node_modules/@babel/core/lib/config/index.js +0 -87
  69. package/dist/node_modules/@babel/core/lib/config/index.js.map +0 -1
  70. package/dist/node_modules/@babel/core/lib/config/item.js +0 -67
  71. package/dist/node_modules/@babel/core/lib/config/item.js.map +0 -1
  72. package/dist/node_modules/@babel/core/lib/config/partial.js +0 -158
  73. package/dist/node_modules/@babel/core/lib/config/partial.js.map +0 -1
  74. package/dist/node_modules/@babel/core/lib/config/pattern-to-regex.js +0 -38
  75. package/dist/node_modules/@babel/core/lib/config/pattern-to-regex.js.map +0 -1
  76. package/dist/node_modules/@babel/core/lib/config/plugin.js +0 -33
  77. package/dist/node_modules/@babel/core/lib/config/plugin.js.map +0 -1
  78. package/dist/node_modules/@babel/core/lib/config/printer.js +0 -113
  79. package/dist/node_modules/@babel/core/lib/config/printer.js.map +0 -1
  80. package/dist/node_modules/@babel/core/lib/config/resolve-targets-browser.js +0 -41
  81. package/dist/node_modules/@babel/core/lib/config/resolve-targets-browser.js.map +0 -1
  82. package/dist/node_modules/@babel/core/lib/config/resolve-targets.js +0 -61
  83. package/dist/node_modules/@babel/core/lib/config/resolve-targets.js.map +0 -1
  84. package/dist/node_modules/@babel/core/lib/config/util.js +0 -31
  85. package/dist/node_modules/@babel/core/lib/config/util.js.map +0 -1
  86. package/dist/node_modules/@babel/core/lib/config/validation/option-assertions.js +0 -277
  87. package/dist/node_modules/@babel/core/lib/config/validation/option-assertions.js.map +0 -1
  88. package/dist/node_modules/@babel/core/lib/config/validation/options.js +0 -187
  89. package/dist/node_modules/@babel/core/lib/config/validation/options.js.map +0 -1
  90. package/dist/node_modules/@babel/core/lib/config/validation/plugins.js +0 -67
  91. package/dist/node_modules/@babel/core/lib/config/validation/plugins.js.map +0 -1
  92. package/dist/node_modules/@babel/core/lib/config/validation/removed.js +0 -68
  93. package/dist/node_modules/@babel/core/lib/config/validation/removed.js.map +0 -1
  94. package/dist/node_modules/@babel/core/lib/errors/config-error.js +0 -18
  95. package/dist/node_modules/@babel/core/lib/errors/config-error.js.map +0 -1
  96. package/dist/node_modules/@babel/core/lib/errors/rewrite-stack-trace.js +0 -98
  97. package/dist/node_modules/@babel/core/lib/errors/rewrite-stack-trace.js.map +0 -1
  98. package/dist/node_modules/@babel/core/lib/gensync-utils/async.js +0 -90
  99. package/dist/node_modules/@babel/core/lib/gensync-utils/async.js.map +0 -1
  100. package/dist/node_modules/@babel/core/lib/gensync-utils/fs.js +0 -31
  101. package/dist/node_modules/@babel/core/lib/gensync-utils/fs.js.map +0 -1
  102. package/dist/node_modules/@babel/core/lib/gensync-utils/functional.js +0 -58
  103. package/dist/node_modules/@babel/core/lib/gensync-utils/functional.js.map +0 -1
  104. package/dist/node_modules/@babel/core/lib/index.js +0 -230
  105. package/dist/node_modules/@babel/core/lib/index.js.map +0 -1
  106. package/dist/node_modules/@babel/core/lib/parse.js +0 -45
  107. package/dist/node_modules/@babel/core/lib/parse.js.map +0 -1
  108. package/dist/node_modules/@babel/core/lib/parser/index.js +0 -85
  109. package/dist/node_modules/@babel/core/lib/parser/index.js.map +0 -1
  110. package/dist/node_modules/@babel/core/lib/parser/util/missing-plugin-helper.js +0 -337
  111. package/dist/node_modules/@babel/core/lib/parser/util/missing-plugin-helper.js.map +0 -1
  112. package/dist/node_modules/@babel/core/lib/tools/build-external-helpers.js +0 -144
  113. package/dist/node_modules/@babel/core/lib/tools/build-external-helpers.js.map +0 -1
  114. package/dist/node_modules/@babel/core/lib/transform-ast.js +0 -48
  115. package/dist/node_modules/@babel/core/lib/transform-ast.js.map +0 -1
  116. package/dist/node_modules/@babel/core/lib/transform-file-browser.js +0 -23
  117. package/dist/node_modules/@babel/core/lib/transform-file-browser.js.map +0 -1
  118. package/dist/node_modules/@babel/core/lib/transform-file.js +0 -40
  119. package/dist/node_modules/@babel/core/lib/transform-file.js.map +0 -1
  120. package/dist/node_modules/@babel/core/lib/transform.js +0 -47
  121. package/dist/node_modules/@babel/core/lib/transform.js.map +0 -1
  122. package/dist/node_modules/@babel/core/lib/transformation/block-hoist-plugin.js +0 -84
  123. package/dist/node_modules/@babel/core/lib/transformation/block-hoist-plugin.js.map +0 -1
  124. package/dist/node_modules/@babel/core/lib/transformation/file/babel-7-helpers.cjs +0 -4
  125. package/dist/node_modules/@babel/core/lib/transformation/file/babel-7-helpers.cjs.map +0 -1
  126. package/dist/node_modules/@babel/core/lib/transformation/file/file.js +0 -204
  127. package/dist/node_modules/@babel/core/lib/transformation/file/file.js.map +0 -1
  128. package/dist/node_modules/@babel/core/lib/transformation/file/generate.js +0 -84
  129. package/dist/node_modules/@babel/core/lib/transformation/file/generate.js.map +0 -1
  130. package/dist/node_modules/@babel/core/lib/transformation/file/merge-map.js +0 -37
  131. package/dist/node_modules/@babel/core/lib/transformation/file/merge-map.js.map +0 -1
  132. package/dist/node_modules/@babel/core/lib/transformation/index.js +0 -90
  133. package/dist/node_modules/@babel/core/lib/transformation/index.js.map +0 -1
  134. package/dist/node_modules/@babel/core/lib/transformation/normalize-file.js +0 -127
  135. package/dist/node_modules/@babel/core/lib/transformation/normalize-file.js.map +0 -1
  136. package/dist/node_modules/@babel/core/lib/transformation/normalize-opts.js +0 -59
  137. package/dist/node_modules/@babel/core/lib/transformation/normalize-opts.js.map +0 -1
  138. package/dist/node_modules/@babel/core/lib/transformation/plugin-pass.js +0 -48
  139. package/dist/node_modules/@babel/core/lib/transformation/plugin-pass.js.map +0 -1
  140. package/dist/node_modules/@babel/core/lib/transformation/util/clone-deep.js +0 -54
  141. package/dist/node_modules/@babel/core/lib/transformation/util/clone-deep.js.map +0 -1
  142. package/dist/node_modules/@babel/core/lib/vendor/import-meta-resolve.js +0 -1042
  143. package/dist/node_modules/@babel/core/lib/vendor/import-meta-resolve.js.map +0 -1
  144. package/dist/node_modules/@babel/core/package.json +0 -82
  145. package/dist/node_modules/@babel/core/src/config/files/index-browser.ts +0 -115
  146. package/dist/node_modules/@babel/core/src/config/files/index.ts +0 -30
  147. package/dist/node_modules/@babel/core/src/config/resolve-targets-browser.ts +0 -42
  148. package/dist/node_modules/@babel/core/src/config/resolve-targets.ts +0 -54
  149. package/dist/node_modules/@babel/core/src/transform-file-browser.ts +0 -33
  150. package/dist/node_modules/@babel/core/src/transform-file.ts +0 -56
  151. package/dist/node_modules/@babel/helper-module-imports/LICENSE +0 -22
  152. package/dist/node_modules/@babel/helper-module-imports/README.md +0 -19
  153. package/dist/node_modules/@babel/helper-module-imports/lib/import-builder.js +0 -122
  154. package/dist/node_modules/@babel/helper-module-imports/lib/import-builder.js.map +0 -1
  155. package/dist/node_modules/@babel/helper-module-imports/lib/import-injector.js +0 -304
  156. package/dist/node_modules/@babel/helper-module-imports/lib/import-injector.js.map +0 -1
  157. package/dist/node_modules/@babel/helper-module-imports/lib/index.js +0 -37
  158. package/dist/node_modules/@babel/helper-module-imports/lib/index.js.map +0 -1
  159. package/dist/node_modules/@babel/helper-module-imports/lib/is-module.js +0 -11
  160. package/dist/node_modules/@babel/helper-module-imports/lib/is-module.js.map +0 -1
  161. package/dist/node_modules/@babel/helper-module-imports/package.json +0 -28
  162. package/dist/node_modules/@babel/plugin-syntax-jsx/LICENSE +0 -22
  163. package/dist/node_modules/@babel/plugin-syntax-jsx/README.md +0 -19
  164. package/dist/node_modules/@babel/plugin-syntax-jsx/lib/index.js +0 -21
  165. package/dist/node_modules/@babel/plugin-syntax-jsx/lib/index.js.map +0 -1
  166. package/dist/node_modules/@babel/plugin-syntax-jsx/package.json +0 -33
  167. package/dist/node_modules/@preact/signals-core/CHANGELOG.md +0 -331
  168. package/dist/node_modules/@preact/signals-core/LICENSE +0 -21
  169. package/dist/node_modules/@preact/signals-core/README.md +0 -258
  170. package/dist/node_modules/@preact/signals-core/dist/signals-core.d.ts +0 -149
  171. package/dist/node_modules/@preact/signals-core/dist/signals-core.js +0 -1
  172. package/dist/node_modules/@preact/signals-core/dist/signals-core.js.map +0 -1
  173. package/dist/node_modules/@preact/signals-core/dist/signals-core.min.js +0 -1
  174. package/dist/node_modules/@preact/signals-core/dist/signals-core.min.js.map +0 -1
  175. package/dist/node_modules/@preact/signals-core/dist/signals-core.mjs +0 -1
  176. package/dist/node_modules/@preact/signals-core/dist/signals-core.mjs.map +0 -1
  177. package/dist/node_modules/@preact/signals-core/dist/signals-core.module.js +0 -1
  178. package/dist/node_modules/@preact/signals-core/dist/signals-core.module.js.map +0 -1
  179. package/dist/node_modules/@preact/signals-core/package.json +0 -48
  180. package/dist/node_modules/@preact/signals-core/src/index.ts +0 -1121
  181. package/dist/package.json +0 -30
  182. package/dist/router/Link.js +0 -73
  183. package/dist/router/index.js +0 -216
  184. package/dist/runtime/For.js +0 -16
  185. package/dist/runtime/dom.js +0 -73
  186. package/dist/runtime/lifecycle.js +0 -25
  187. package/dist/runtime/props.js +0 -103
@@ -0,0 +1,121 @@
1
+ // Hydration primitives (Phase 2 — see docs/HYDRATION.md). The client *adopts* the
2
+ // server-rendered DOM instead of rebuilding it: it claims existing nodes and wires
3
+ // reactivity (bindText/bindAttr/events from dom.js) onto them. There is no separate
4
+ // "hydrate" binding logic — those helpers already operate on existing nodes; the only
5
+ // new work is *node acquisition*, which is what this module provides.
6
+ //
7
+ // Acquisition is a **cursor walk** (the decided approach, like Solid): the Hydrate
8
+ // codegen emits claim calls in the exact order the SSG backend emitted nodes, so a
9
+ // cursor over a parent's childNodes lines up 1:1 with the template. The server output
10
+ // carries no inter-element whitespace (ssg.rs concatenates with no padding), so the
11
+ // re-parsed DOM has text nodes only where the template does — the walk stays aligned.
12
+ //
13
+ // Markers (only where structure is variable): a dynamic text hole is delimited by
14
+ // `<!--$-->value<!--/-->` so it is findable even when the value is empty or adjacent
15
+ // to static text (which the HTML parser would otherwise merge into one text node).
16
+ // Static structure carries no markers and is claimed by position.
17
+
18
+ /** Comment markers bounding a dynamic text hole in server HTML. */
19
+ export const HOLE_START = "$";
20
+ export const HOLE_END = "/";
21
+
22
+ const ELEMENT = 1;
23
+ const TEXT = 3;
24
+ const COMMENT = 8;
25
+
26
+ /** Thrown when the server DOM doesn't match the template at a claim site. The
27
+ * component boundary catches this and recovers by rebuilding via CSR (never silent;
28
+ * see docs/HYDRATION.md §3.5). */
29
+ export class HydrationMismatch extends Error {
30
+ constructor(message) {
31
+ super(message);
32
+ this.name = "HydrationMismatch";
33
+ }
34
+ }
35
+
36
+ // ── the hydration flag ────────────────────────────────────────────────────────
37
+ // True during the initial hydration pass. A component's connectedCallback reads it
38
+ // to decide whether to *adopt* its server children or *build* a fresh subtree — the
39
+ // custom-element-as-island coordination (docs/HYDRATION.md §3.4).
40
+ let _hydrating = false;
41
+
42
+ /** Is the client mid-hydration right now? */
43
+ export function isHydrating() {
44
+ return _hydrating;
45
+ }
46
+
47
+ /** Run `fn` with the hydration flag set, restoring the prior value (nesting-safe). */
48
+ export function runHydration(fn) {
49
+ const prev = _hydrating;
50
+ _hydrating = true;
51
+ try {
52
+ return fn();
53
+ } finally {
54
+ _hydrating = prev;
55
+ }
56
+ }
57
+
58
+ // ── the cursor ────────────────────────────────────────────────────────────────
59
+
60
+ /** A walker over `parent`'s children, positioned at the next node to claim. */
61
+ export function cursor(parent) {
62
+ return { node: parent.firstChild };
63
+ }
64
+
65
+ /** Advance past the next node without claiming it (a static text node between
66
+ * dynamic siblings), returning it. */
67
+ export function skipNode(cur) {
68
+ const n = cur.node;
69
+ cur.node = n ? n.nextSibling : null;
70
+ return n;
71
+ }
72
+
73
+ /**
74
+ * Claim the next node as an element, optionally asserting its tag, and advance the
75
+ * cursor past it. Returns the element so the caller can claim its children with a
76
+ * fresh `cursor(el)`. A wrong/absent node throws {@link HydrationMismatch}.
77
+ */
78
+ export function claimElement(cur, tag) {
79
+ const el = cur.node;
80
+ if (!el || el.nodeType !== ELEMENT || (tag && el.tagName.toLowerCase() !== tag.toLowerCase())) {
81
+ throw new HydrationMismatch(`expected <${tag ?? "element"}>, found ${describe(el)}`);
82
+ }
83
+ cur.node = el.nextSibling;
84
+ return el;
85
+ }
86
+
87
+ /**
88
+ * Claim a dynamic text hole delimited by `<!--$-->…<!--/-->`, returning the text node
89
+ * to bind onto (created empty when the server rendered no value), and advance the
90
+ * cursor past the closing marker. Wire reactivity with `bindText(node, fn)` from
91
+ * dom.js — the binding logic is shared with CSR; only the node is adopted, not built.
92
+ */
93
+ export function claimText(cur) {
94
+ const start = cur.node;
95
+ if (!start || start.nodeType !== COMMENT || start.data !== HOLE_START) {
96
+ throw new HydrationMismatch(`expected a text-hole marker, found ${describe(start)}`);
97
+ }
98
+ let node = start.nextSibling;
99
+ let textNode = null;
100
+ while (node && !(node.nodeType === COMMENT && node.data === HOLE_END)) {
101
+ if (node.nodeType === TEXT && !textNode) textNode = node;
102
+ node = node.nextSibling;
103
+ }
104
+ const end = node; // the `<!--/-->` marker (or null if the markup is malformed)
105
+ if (!textNode) {
106
+ // Empty hole (`<!--$--><!--/-->`): synthesize the anchor bindText will write into.
107
+ textNode = document.createTextNode("");
108
+ (start.parentNode || document).insertBefore(textNode, end);
109
+ }
110
+ cur.node = end ? end.nextSibling : null;
111
+ return textNode;
112
+ }
113
+
114
+ /** A short human description of a node for mismatch messages. */
115
+ function describe(node) {
116
+ if (!node) return "nothing";
117
+ if (node.nodeType === ELEMENT) return `<${node.tagName.toLowerCase()}>`;
118
+ if (node.nodeType === TEXT) return `text "${(node.data || "").slice(0, 20)}"`;
119
+ if (node.nodeType === COMMENT) return `comment <!--${node.data}-->`;
120
+ return `node(${node.nodeType})`;
121
+ }
@@ -0,0 +1,22 @@
1
+ // Built-in custom elements self-register on import. They're used in JSX as *tags*
2
+ // (`<Portal>` → `web-internal-portal`), so their bindings are never referenced and a plain
3
+ // re-export would be tree-shaken away (the element never registers). Bare imports
4
+ // force the registration side effect to be retained.
5
+ import "./context.js";
6
+ import "./error-boundary.js";
7
+ import "./portal.js";
8
+ import "./raw-html.js";
9
+ import "./code-block.js";
10
+
11
+ export * from "./dom.js";
12
+ export * from "./events.js";
13
+ export * from "./mount.js";
14
+ export * from "./hydrate.js";
15
+ export * from "./lifecycle.js";
16
+ export * from "./router.js";
17
+ export * from "./context.js";
18
+ export * from "./error-boundary.js";
19
+ export * from "./portal.js";
20
+ export * from "./raw-html.js";
21
+ export * from "./code-block.js";
22
+ export * from "./clipboard.js";
@@ -0,0 +1,9 @@
1
+ // Lifecycle hooks. `onMount`/`onCleanup` are compiler macros: the OpenTF
2
+ // compiler collects their callbacks and wires them into a component's
3
+ // connect/disconnect (Custom Element) or a page/layout factory's `__lifecycle`
4
+ // record. These exports exist so the source-level `import { onMount } from
5
+ // "@opentf/web"` resolves, and so calling them outside a compiled component (or
6
+ // during SSR) is a safe no-op rather than a crash.
7
+
8
+ export function onMount() {}
9
+ export function onCleanup() {}
@@ -0,0 +1,49 @@
1
+ // Mounting a factory-style view (pages/layouts compile to factory functions;
2
+ // SPEC §2.2). Components compile to Custom Elements and mount via the DOM.
3
+
4
+ import { reportError } from "../core/errors.js";
5
+
6
+ /**
7
+ * Mount a view into `target`. `view` is either a factory function returning a
8
+ * DOM node or an already-built node. Returns the mounted node.
9
+ *
10
+ * Page/layout factories may attach a `__lifecycle` record (`{ mounts, cleanups }`)
11
+ * built from `onMount`/`onCleanup`. After insertion we run the `onMount` callbacks,
12
+ * collecting any returned disposer into `cleanups` so a caller (e.g. the router)
13
+ * can tear the page down on navigation.
14
+ */
15
+ export function mount(view, target) {
16
+ const node = typeof view === "function" ? view() : view;
17
+ target.appendChild(node);
18
+ runMount(node);
19
+ return node;
20
+ }
21
+
22
+ /** Run a node's `onMount` callbacks, collecting returned disposers as cleanups. */
23
+ export function runMount(node) {
24
+ const lc = node && node.__lifecycle;
25
+ if (!lc) return;
26
+ for (const cb of lc.mounts) {
27
+ try {
28
+ const disposer = cb();
29
+ if (typeof disposer === "function") lc.cleanups.push(disposer);
30
+ } catch (e) {
31
+ reportError(e, { phase: "mount" });
32
+ }
33
+ }
34
+ lc.mounts = [];
35
+ }
36
+
37
+ /** Run a node's collected `onCleanup`/disposer teardown (idempotent). */
38
+ export function runCleanup(node) {
39
+ const lc = node && node.__lifecycle;
40
+ if (!lc) return;
41
+ for (const cb of lc.cleanups) {
42
+ try {
43
+ cb();
44
+ } catch (e) {
45
+ console.error(e);
46
+ }
47
+ }
48
+ lc.cleanups = [];
49
+ }
@@ -0,0 +1,59 @@
1
+ //! Portal — render a subtree into a different DOM location (a modal/tooltip/toast
2
+ //! escaping an ancestor's overflow/z-index/transform).
3
+ //
4
+ // <Portal to="#toast-root"> … </Portal> // selector, element, or default body
5
+ //
6
+ // `<Portal>` compiles to this built-in `web-internal-portal` element (capitalized name →
7
+ // tag, like <ContextProvider>) — no compiler changes. At connect it moves its
8
+ // light-DOM children to the target; on disconnect it removes them so their
9
+ // custom-element disconnect + effect cleanups run (the part userland leaks).
10
+ //
11
+ // Context is preserved across the boundary: each relocated root is marked
12
+ // `data-otfw-portal` with a `__portalHost` back-pointer to this element, and
13
+ // `readContext` (context.js) hops across it. Note that for modals/dialogs the
14
+ // native top layer (`<dialog>.showModal()`, the popover API) is usually a better
15
+ // fit than a portal — Portal is the general escape hatch for everything else.
16
+
17
+ function resolveTarget(to) {
18
+ if (to && to.nodeType === 1) return to; // an element
19
+ if (typeof to === "string" && to) return document.querySelector(to) || document.body;
20
+ return document.body;
21
+ }
22
+
23
+ export class PortalElement extends HTMLElement {
24
+ set to(v) {
25
+ this._to = v;
26
+ }
27
+ get to() {
28
+ return this._to;
29
+ }
30
+
31
+ connectedCallback() {
32
+ if (this._mounted) return;
33
+ this._mounted = true;
34
+ const target = resolveTarget(this._to);
35
+ // Snapshot children, mark each element root for the context hop, then move
36
+ // them — markers must be set before the move connects them at the target.
37
+ this._moved = Array.from(this.childNodes);
38
+ for (const n of this._moved) {
39
+ if (n.nodeType === 1) {
40
+ n.__portalHost = this;
41
+ n.setAttribute("data-otfw-portal", "");
42
+ }
43
+ }
44
+ for (const n of this._moved) target.appendChild(n);
45
+ }
46
+
47
+ disconnectedCallback() {
48
+ for (const n of this._moved || []) n.remove(); // triggers descendants' cleanup
49
+ this._moved = [];
50
+ this._mounted = false;
51
+ }
52
+ }
53
+
54
+ // Exported so `import { Portal } from "@opentf/web"` resolves; the import side
55
+ // effect registers the element (JSX uses the `web-internal-portal` tag).
56
+ export const Portal = PortalElement;
57
+ if (typeof customElements !== "undefined" && !customElements.get("web-internal-portal")) {
58
+ customElements.define("web-internal-portal", PortalElement);
59
+ }
@@ -0,0 +1,33 @@
1
+ //! RawHtml — render a trusted HTML string as light-DOM content.
2
+ //
3
+ // <RawHtml html={"<pre>…</pre>"} /> // compiles to the built-in `web-internal-raw-html`
4
+ //
5
+ // Used by the MDX front-end for build-time syntax-highlighted code blocks: the
6
+ // highlighted markup is static (no reactivity), so the element just assigns its
7
+ // `html` property to `innerHTML`. The string is treated as trusted — it comes from
8
+ // the compiler, not user input. SSG renders the string inline (server/builtins.js).
9
+
10
+ export class RawHtmlElement extends HTMLElement {
11
+ set html(v) {
12
+ this._html = v;
13
+ if (this.isConnected) this.innerHTML = v == null ? "" : String(v);
14
+ }
15
+ get html() {
16
+ return this._html;
17
+ }
18
+
19
+ connectedCallback() {
20
+ // Apply once on connect (the property is set before append during CSR build).
21
+ if (this._html != null && !this._applied) {
22
+ this.innerHTML = String(this._html);
23
+ this._applied = true;
24
+ }
25
+ }
26
+ }
27
+
28
+ // Exported so `import { RawHtml } from "@opentf/web"` resolves; the import side
29
+ // effect registers the element (JSX uses the `web-internal-raw-html` tag).
30
+ export const RawHtml = RawHtmlElement;
31
+ if (typeof customElements !== "undefined" && !customElements.get("web-internal-raw-html")) {
32
+ customElements.define("web-internal-raw-html", RawHtmlElement);
33
+ }