@barefootjs/hono 0.1.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 (81) hide show
  1. package/dist/adapter/hono-adapter.d.ts +141 -0
  2. package/dist/adapter/hono-adapter.d.ts.map +1 -0
  3. package/dist/adapter/index.d.ts +6 -0
  4. package/dist/adapter/index.d.ts.map +1 -0
  5. package/dist/adapter/index.js +632 -0
  6. package/dist/app.d.ts +131 -0
  7. package/dist/app.d.ts.map +1 -0
  8. package/dist/app.js +139 -0
  9. package/dist/async.d.ts +15 -0
  10. package/dist/async.d.ts.map +1 -0
  11. package/dist/async.js +12 -0
  12. package/dist/build.d.ts +65 -0
  13. package/dist/build.d.ts.map +1 -0
  14. package/dist/build.js +785 -0
  15. package/dist/client-shim.d.ts +59 -0
  16. package/dist/client-shim.d.ts.map +1 -0
  17. package/dist/client-shim.js +90 -0
  18. package/dist/dev-worker.d.ts +25 -0
  19. package/dist/dev-worker.d.ts.map +1 -0
  20. package/dist/dev-worker.js +65 -0
  21. package/dist/dev.d.ts +36 -0
  22. package/dist/dev.d.ts.map +1 -0
  23. package/dist/dev.js +418 -0
  24. package/dist/dialog-context.d.ts +13 -0
  25. package/dist/dialog-context.d.ts.map +1 -0
  26. package/dist/dialog-context.js +10 -0
  27. package/dist/index.d.ts +13 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +632 -0
  30. package/dist/jsx/jsx-dev-runtime/index.d.ts +9 -0
  31. package/dist/jsx/jsx-dev-runtime/index.d.ts.map +1 -0
  32. package/dist/jsx/jsx-dev-runtime/index.js +6 -0
  33. package/dist/jsx/jsx-runtime/index.d.ts +32 -0
  34. package/dist/jsx/jsx-runtime/index.d.ts.map +1 -0
  35. package/dist/jsx/jsx-runtime/index.js +10 -0
  36. package/dist/portal-ssr.d.ts +22 -0
  37. package/dist/portal-ssr.d.ts.map +1 -0
  38. package/dist/portal-ssr.js +73 -0
  39. package/dist/portals.d.ts +26 -0
  40. package/dist/portals.d.ts.map +1 -0
  41. package/dist/portals.js +41 -0
  42. package/dist/preload.d.ts +56 -0
  43. package/dist/preload.d.ts.map +1 -0
  44. package/dist/preload.js +51 -0
  45. package/dist/scripts.d.ts +80 -0
  46. package/dist/scripts.d.ts.map +1 -0
  47. package/dist/scripts.js +198 -0
  48. package/dist/test-render.d.ts +28 -0
  49. package/dist/test-render.d.ts.map +1 -0
  50. package/dist/utils.d.ts +16 -0
  51. package/dist/utils.d.ts.map +1 -0
  52. package/dist/utils.js +16 -0
  53. package/package.json +116 -0
  54. package/src/__tests__/async.test.tsx +106 -0
  55. package/src/__tests__/bfscripts-entry-roots.test.tsx +135 -0
  56. package/src/__tests__/build.test.ts +299 -0
  57. package/src/__tests__/dev.test.tsx +123 -0
  58. package/src/__tests__/hydration-props-type.test.ts +141 -0
  59. package/src/__tests__/manifest-scripts.test.ts +87 -0
  60. package/src/__tests__/scaffold.test.ts +209 -0
  61. package/src/__tests__/ssr-context-bridge.test.ts +110 -0
  62. package/src/__tests__/string-literal-css-var-prop.test.ts +84 -0
  63. package/src/__tests__/stub-deps-scripts.test.ts +183 -0
  64. package/src/adapter/hono-adapter.ts +1114 -0
  65. package/src/adapter/index.ts +6 -0
  66. package/src/app.ts +220 -0
  67. package/src/async.tsx +55 -0
  68. package/src/build.ts +230 -0
  69. package/src/client-shim.ts +164 -0
  70. package/src/dev-worker.ts +93 -0
  71. package/src/dev.tsx +146 -0
  72. package/src/dialog-context.tsx +44 -0
  73. package/src/index.ts +26 -0
  74. package/src/jsx/jsx-dev-runtime/index.ts +9 -0
  75. package/src/jsx/jsx-runtime/index.ts +40 -0
  76. package/src/portal-ssr.tsx +92 -0
  77. package/src/portals.tsx +98 -0
  78. package/src/preload.tsx +166 -0
  79. package/src/scripts.tsx +220 -0
  80. package/src/test-render.ts +143 -0
  81. package/src/utils.ts +26 -0
@@ -0,0 +1,10 @@
1
+ // src/jsx/jsx-runtime/index.ts
2
+ import { jsx, jsxs, Fragment, jsxAttr, jsxEscape, jsxTemplate } from "hono/jsx/jsx-runtime";
3
+ export {
4
+ jsxs,
5
+ jsxTemplate,
6
+ jsxEscape,
7
+ jsxAttr,
8
+ jsx,
9
+ Fragment
10
+ };
@@ -0,0 +1,22 @@
1
+ import type { Child } from 'hono/jsx';
2
+ /**
3
+ * Reset portal counter (for testing or explicit reset).
4
+ */
5
+ export declare function resetPortalCounter(): void;
6
+ export interface PortalProps {
7
+ children: Child;
8
+ scopeId?: string;
9
+ }
10
+ /**
11
+ * Portal component that moves children to document.body during SSR.
12
+ *
13
+ * During SSR:
14
+ * - Collects content for BfPortals output (before BfPortals renders)
15
+ * - Returns placeholder <template> for hydration matching
16
+ * - If BfPortals already rendered (Suspense), outputs inline
17
+ *
18
+ * On client:
19
+ * - Renders children normally (createPortal moves them later)
20
+ */
21
+ export declare function Portal(props: PortalProps): import("hono/jsx/jsx-dev-runtime").JSX.Element;
22
+ //# sourceMappingURL=portal-ssr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal-ssr.d.ts","sourceRoot":"","sources":["../src/portal-ssr.tsx"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AAarC;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,KAAK,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,kDA4BxC"}
@@ -0,0 +1,73 @@
1
+ // src/portals.tsx
2
+ import { useRequestContext } from "hono/jsx-renderer";
3
+ import { Fragment } from "hono/jsx";
4
+ import { jsxDEV } from "hono/jsx/jsx-dev-runtime";
5
+ function collectPortal(id, scopeId, content) {
6
+ try {
7
+ const c = useRequestContext();
8
+ const portals = c.get("bfCollectedPortals") || [];
9
+ portals.push({ id, scopeId, content });
10
+ c.set("bfCollectedPortals", portals);
11
+ } catch {}
12
+ }
13
+ function isPortalsRendered() {
14
+ try {
15
+ const c = useRequestContext();
16
+ return c.get("bfPortalsRendered") ?? false;
17
+ } catch {
18
+ return true;
19
+ }
20
+ }
21
+ function BfPortals() {
22
+ try {
23
+ const c = useRequestContext();
24
+ c.set("bfPortalsRendered", true);
25
+ const portals = c.get("bfCollectedPortals") || [];
26
+ return /* @__PURE__ */ jsxDEV(Fragment, {
27
+ children: portals.map(({ id, scopeId, content }) => /* @__PURE__ */ jsxDEV("div", {
28
+ "bf-pi": id,
29
+ "bf-po": scopeId,
30
+ children: content
31
+ }, id, false, undefined, this))
32
+ }, undefined, false, undefined, this);
33
+ } catch {
34
+ return null;
35
+ }
36
+ }
37
+
38
+ // src/portal-ssr.tsx
39
+ import { useRequestContext as useRequestContext2 } from "hono/jsx-renderer";
40
+ import { Fragment as Fragment2 } from "hono/jsx";
41
+ import { jsxDEV as jsxDEV2 } from "hono/jsx/jsx-dev-runtime";
42
+ var portalCounter = 0;
43
+ function generatePortalId() {
44
+ return `bf-portal-${++portalCounter}`;
45
+ }
46
+ function resetPortalCounter() {
47
+ portalCounter = 0;
48
+ }
49
+ function Portal(props) {
50
+ const portalId = generatePortalId();
51
+ try {
52
+ useRequestContext2();
53
+ if (isPortalsRendered()) {
54
+ return /* @__PURE__ */ jsxDEV2("div", {
55
+ "bf-pi": portalId,
56
+ "bf-po": props.scopeId || "",
57
+ children: props.children
58
+ }, undefined, false, undefined, this);
59
+ }
60
+ collectPortal(portalId, props.scopeId || "", props.children);
61
+ return /* @__PURE__ */ jsxDEV2("template", {
62
+ "bf-pp": portalId
63
+ }, undefined, false, undefined, this);
64
+ } catch {
65
+ return /* @__PURE__ */ jsxDEV2(Fragment2, {
66
+ children: props.children
67
+ }, undefined, false, undefined, this);
68
+ }
69
+ }
70
+ export {
71
+ resetPortalCounter,
72
+ Portal
73
+ };
@@ -0,0 +1,26 @@
1
+ import type { Child } from 'hono/jsx';
2
+ export type CollectedPortal = {
3
+ id: string;
4
+ scopeId: string;
5
+ content: Child;
6
+ };
7
+ /**
8
+ * Collect portal content for SSR output.
9
+ * Called by Portal component during SSR rendering.
10
+ */
11
+ export declare function collectPortal(id: string, scopeId: string, content: Child): void;
12
+ /**
13
+ * Check if BfPortals has already been rendered.
14
+ * Used by Portal component to determine if it should output inline.
15
+ */
16
+ export declare function isPortalsRendered(): boolean;
17
+ /**
18
+ * Renders all collected portal content.
19
+ * Place this component at the end of your <body> element, before BfScripts.
20
+ *
21
+ * After rendering, sets 'bfPortalsRendered' flag to true.
22
+ * Portal components rendered after BfPortals (e.g., inside Suspense boundaries)
23
+ * will check this flag and output their content inline instead.
24
+ */
25
+ export declare function BfPortals(): import("hono/jsx/jsx-dev-runtime").JSX.Element | null;
26
+ //# sourceMappingURL=portals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portals.d.ts","sourceRoot":"","sources":["../src/portals.tsx"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAA;AAErC,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,KAAK,CAAA;CACf,CAAA;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,GAAG,IAAI,CAS/E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAQ3C;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,0DAwBxB"}
@@ -0,0 +1,41 @@
1
+ // src/portals.tsx
2
+ import { useRequestContext } from "hono/jsx-renderer";
3
+ import { Fragment } from "hono/jsx";
4
+ import { jsxDEV } from "hono/jsx/jsx-dev-runtime";
5
+ function collectPortal(id, scopeId, content) {
6
+ try {
7
+ const c = useRequestContext();
8
+ const portals = c.get("bfCollectedPortals") || [];
9
+ portals.push({ id, scopeId, content });
10
+ c.set("bfCollectedPortals", portals);
11
+ } catch {}
12
+ }
13
+ function isPortalsRendered() {
14
+ try {
15
+ const c = useRequestContext();
16
+ return c.get("bfPortalsRendered") ?? false;
17
+ } catch {
18
+ return true;
19
+ }
20
+ }
21
+ function BfPortals() {
22
+ try {
23
+ const c = useRequestContext();
24
+ c.set("bfPortalsRendered", true);
25
+ const portals = c.get("bfCollectedPortals") || [];
26
+ return /* @__PURE__ */ jsxDEV(Fragment, {
27
+ children: portals.map(({ id, scopeId, content }) => /* @__PURE__ */ jsxDEV("div", {
28
+ "bf-pi": id,
29
+ "bf-po": scopeId,
30
+ children: content
31
+ }, id, false, undefined, this))
32
+ }, undefined, false, undefined, this);
33
+ } catch {
34
+ return null;
35
+ }
36
+ }
37
+ export {
38
+ isPortalsRendered,
39
+ collectPortal,
40
+ BfPortals
41
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Manifest entry type for dependency tracking.
3
+ */
4
+ export interface ManifestEntry {
5
+ markedTemplate: string;
6
+ clientJs?: string;
7
+ props?: Array<{
8
+ name: string;
9
+ type: string;
10
+ optional: boolean;
11
+ }>;
12
+ dependencies?: string[];
13
+ }
14
+ /**
15
+ * Manifest type mapping component names to their metadata.
16
+ */
17
+ export type Manifest = Record<string, ManifestEntry>;
18
+ export interface BfPreloadProps {
19
+ /**
20
+ * Path to static files directory.
21
+ * @default '/static'
22
+ */
23
+ staticPath?: string;
24
+ /**
25
+ * Additional script URLs to preload.
26
+ * These are added in addition to the barefoot runtime.
27
+ */
28
+ scripts?: string[];
29
+ /**
30
+ * Whether to preload the barefoot runtime.
31
+ * @default true
32
+ */
33
+ includeRuntime?: boolean;
34
+ /**
35
+ * Component manifest with dependency information.
36
+ * Used for automatic dependency chain preloading.
37
+ */
38
+ manifest?: Manifest;
39
+ /**
40
+ * Component names to preload with their dependencies.
41
+ * Requires manifest to be provided.
42
+ */
43
+ components?: string[];
44
+ }
45
+ /**
46
+ * Renders modulepreload link tags for BarefootJS scripts.
47
+ * Place this component in your <head> element.
48
+ *
49
+ * By default, preloads the barefoot.js runtime which is required
50
+ * by all BarefootJS components.
51
+ *
52
+ * When manifest and components props are provided, automatically
53
+ * preloads the full dependency chain for those components.
54
+ */
55
+ export declare function BfPreload({ staticPath, scripts, includeRuntime, manifest, components }?: BfPreloadProps): import("hono/jsx/jsx-dev-runtime").JSX.Element;
56
+ //# sourceMappingURL=preload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preload.d.ts","sourceRoot":"","sources":["../src/preload.tsx"],"names":[],"mappings":"AAmCA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;IAChE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CACxB;AAED;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;AAEpD,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAElB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IAExB;;;OAGG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAA;IAEnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CACtB;AAwCD;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,EACxB,UAAsB,EACtB,OAAY,EACZ,cAAqB,EACrB,QAAQ,EACR,UAAe,EAChB,GAAE,cAAmB,kDA6BrB"}
@@ -0,0 +1,51 @@
1
+ // src/preload.tsx
2
+ import { Fragment } from "hono/jsx";
3
+ import { jsxDEV } from "hono/jsx/jsx-dev-runtime";
4
+ function resolveDependencyChain(components, manifest, visited = new Set) {
5
+ const result = [];
6
+ for (const compName of components) {
7
+ if (visited.has(compName))
8
+ continue;
9
+ visited.add(compName);
10
+ const entry = manifest[compName];
11
+ if (!entry)
12
+ continue;
13
+ if (entry.clientJs) {
14
+ result.push(entry.clientJs);
15
+ }
16
+ if (entry.dependencies && entry.dependencies.length > 0) {
17
+ const childScripts = resolveDependencyChain(entry.dependencies, manifest, visited);
18
+ result.push(...childScripts);
19
+ }
20
+ }
21
+ return result;
22
+ }
23
+ function BfPreload({
24
+ staticPath = "/static",
25
+ scripts = [],
26
+ includeRuntime = true,
27
+ manifest,
28
+ components = []
29
+ } = {}) {
30
+ const urls = [];
31
+ if (includeRuntime) {
32
+ urls.push(`${staticPath}/components/barefoot.js`);
33
+ }
34
+ if (manifest && components.length > 0) {
35
+ const dependencyScripts = resolveDependencyChain(components, manifest);
36
+ for (const script of dependencyScripts) {
37
+ urls.push(`${staticPath}/${script}`);
38
+ }
39
+ }
40
+ urls.push(...scripts);
41
+ const uniqueUrls = [...new Set(urls)];
42
+ return /* @__PURE__ */ jsxDEV(Fragment, {
43
+ children: uniqueUrls.map((url) => /* @__PURE__ */ jsxDEV("link", {
44
+ rel: "modulepreload",
45
+ href: url
46
+ }, undefined, false, undefined, this))
47
+ }, undefined, false, undefined, this);
48
+ }
49
+ export {
50
+ BfPreload
51
+ };
@@ -0,0 +1,80 @@
1
+ import { type BarefootBuildManifest } from './app';
2
+ export type CollectedScript = {
3
+ src: string;
4
+ };
5
+ export interface BfScriptsProps {
6
+ /**
7
+ * Build manifest from `dist/components/manifest.json`. When supplied
8
+ * alongside `base`, the component follows each rendered entry's
9
+ * `stubDeps` transitively and emits a `<script>` for every reachable
10
+ * `.client.js` — necessary for pages that only touch a child
11
+ * component through an imperative stub call (issue #1243).
12
+ *
13
+ * When omitted, behavior matches the pre-#1243 collector: only
14
+ * components whose SSR function executed get a script tag.
15
+ */
16
+ manifest?: BarefootBuildManifest;
17
+ /**
18
+ * URL base where the component bundles are served (e.g.
19
+ * `/static/components/`). Required when `manifest` is supplied —
20
+ * stubDep entries store dist-relative paths and the component
21
+ * needs the URL prefix to emit a working `<script src>`.
22
+ */
23
+ base?: string;
24
+ /**
25
+ * Extra manifest keys to treat as walk roots in addition to the
26
+ * SSR-rendered set. Use this for pages that mount a `'use client'`
27
+ * component via an inline `<script type="module">import "X.client.js"; render(root, "X", …)`
28
+ * instead of SSR'ing `<X />` directly. Without `entryRoots`, the
29
+ * walker has no anchor for `X`'s `stubDeps` and any sibling `'use
30
+ * client'` `.tsx` reached only through the imperative
31
+ * `createComponent` stub rewrite (#1240) never ships as a
32
+ * `<script>`, leaving the runtime registry empty and rendering
33
+ * `[ComponentName]` placeholders.
34
+ *
35
+ * The caller's inline `<script type="module">import …</script>`
36
+ * already loads the root bundle, so the root itself is *not*
37
+ * emitted as a separate `<script src>` — only the transitively
38
+ * reached `stubDeps` are. See #1431.
39
+ */
40
+ entryRoots?: string[];
41
+ }
42
+ /**
43
+ * Renders all collected BarefootJS script tags.
44
+ * Place this component at the end of your <body> element.
45
+ *
46
+ * After rendering, sets 'bfScriptsRendered' flag to true.
47
+ * Components rendered after BfScripts (e.g., inside Suspense boundaries)
48
+ * will check this flag and output their scripts inline instead of
49
+ * collecting them here.
50
+ */
51
+ export declare function BfScripts(props?: BfScriptsProps): import("hono/jsx/jsx-dev-runtime").JSX.Element | null;
52
+ /**
53
+ * Walk stub-rewrite edges from each manifest entry in `roots`,
54
+ * returning the script URLs for every transitively reachable
55
+ * `.client.js` in **DFS post-order** — every dep precedes its
56
+ * dependent in iteration order. Skips any name already present in
57
+ * `excluded` (these have a script tag elsewhere). Mutates `excluded`
58
+ * to record every dep that's been resolved so the caller can pass
59
+ * it to the next SSR pass without double-emitting. Exported for tests.
60
+ *
61
+ * Typical call shape: `roots` ⊆ `excluded`. The caller passes the
62
+ * set of components whose SSR function already pushed a `<script>`
63
+ * (`bfOutputScripts`) as BOTH arguments — "these are already
64
+ * emitted, now walk their stubDeps." A `roots` value already in
65
+ * `excluded` is still walked (we need its `stubDeps`); a `dep`
66
+ * already in `excluded` is recorded as visited but not re-emitted.
67
+ *
68
+ * Why post-order: `<script type="module">` tags evaluate in document
69
+ * order with microtask checkpoints between them, so the first
70
+ * script's `hydrate()`-scheduled walk fires before any later
71
+ * module loads. A chain A→B→C must ship C, B, then A's bundle —
72
+ * BFS order (B, C) would let B's hydration call
73
+ * `createComponent('C', ...)` against an empty registry. Post-order
74
+ * also handles DAG edges like A→B, A→C, C→B correctly
75
+ * (deepest-first, dependencies-first).
76
+ *
77
+ * Cycle-safe: a visited set short-circuits any A → B → A loop.
78
+ */
79
+ export declare function collectStubDepScripts(manifest: BarefootBuildManifest, base: string, roots: Iterable<string>, excluded: Set<string>): Map<string, CollectedScript>;
80
+ //# sourceMappingURL=scripts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scripts.d.ts","sourceRoot":"","sources":["../src/scripts.tsx"],"names":[],"mappings":"AA0CA,OAAO,EAA6B,KAAK,qBAAqB,EAAE,MAAM,OAAO,CAAA;AAE7E,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;OASG;IACH,QAAQ,CAAC,EAAE,qBAAqB,CAAA;IAChC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;;;;;;;;;OAeG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,KAAK,GAAE,cAAmB,yDAgEnD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,qBAAqB,EAC/B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,EACvB,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,GACpB,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CA0B9B"}
@@ -0,0 +1,198 @@
1
+ // src/dev-worker.ts
2
+ var HEARTBEAT_MS = 5000;
3
+ var BOOT_ID = generateBootId();
4
+ function generateBootId() {
5
+ try {
6
+ return crypto.randomUUID();
7
+ } catch {
8
+ return Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
9
+ }
10
+ }
11
+ function isDevDefault() {
12
+ return true;
13
+ }
14
+ function createDevReloader(options = {}) {
15
+ const { enabled = isDevDefault() } = options;
16
+ return (c) => {
17
+ if (!enabled)
18
+ return c.notFound();
19
+ const lastEventId = (c.req.header("Last-Event-ID") ?? "").trim();
20
+ const signal = c.req.raw.signal;
21
+ const stream = new ReadableStream({
22
+ start(controller) {
23
+ const encoder = new TextEncoder;
24
+ const send = (chunk) => {
25
+ try {
26
+ controller.enqueue(encoder.encode(chunk));
27
+ } catch {}
28
+ };
29
+ send(`retry: 1000
30
+
31
+ `);
32
+ const event = lastEventId && lastEventId !== BOOT_ID ? "reload" : "hello";
33
+ send(`event: ${event}
34
+ id: ${BOOT_ID}
35
+ data: ${BOOT_ID}
36
+
37
+ `);
38
+ const heartbeat = setInterval(() => send(`: hb
39
+
40
+ `), HEARTBEAT_MS);
41
+ const onAbort = () => {
42
+ clearInterval(heartbeat);
43
+ try {
44
+ controller.close();
45
+ } catch {}
46
+ };
47
+ if (signal.aborted)
48
+ onAbort();
49
+ else
50
+ signal.addEventListener("abort", onAbort, { once: true });
51
+ }
52
+ });
53
+ return new Response(stream, {
54
+ headers: {
55
+ "Content-Type": "text/event-stream",
56
+ "Cache-Control": "no-cache, no-transform",
57
+ Connection: "keep-alive",
58
+ "X-Accel-Buffering": "no"
59
+ }
60
+ });
61
+ };
62
+ }
63
+
64
+ // src/app.ts
65
+ import { html, raw } from "hono/html";
66
+ import { useRequestContext } from "hono/jsx-renderer";
67
+ var DEV_RELOAD_ENDPOINT_KEY = "bfDevReloadEndpoint";
68
+ function manifestToScriptUrls(manifest, base) {
69
+ const out = [];
70
+ const prefix = `${base.replace(/\/$/, "")}/`;
71
+ if (manifest.__barefoot__?.clientJs) {
72
+ out.push(prefix + relPathFromComponentsBase(manifest.__barefoot__.clientJs));
73
+ }
74
+ for (const [name, entry] of Object.entries(manifest)) {
75
+ if (name === "__barefoot__")
76
+ continue;
77
+ if (entry?.clientJs)
78
+ out.push(prefix + relPathFromComponentsBase(entry.clientJs));
79
+ }
80
+ return out;
81
+ }
82
+ function relPathFromComponentsBase(p) {
83
+ return p.startsWith("components/") ? p.slice("components/".length) : p;
84
+ }
85
+ function BfImportMap(props) {
86
+ const base = props.base.replace(/\/$/, "");
87
+ const json = JSON.stringify({
88
+ imports: {
89
+ "@barefootjs/client": `${base}/barefoot.js`,
90
+ "@barefootjs/client/runtime": `${base}/barefoot.js`
91
+ }
92
+ });
93
+ return html`<script type="importmap">${raw(json)}</script>`;
94
+ }
95
+ var __bfEmptyManifestWarned = false;
96
+ function BfScripts(props) {
97
+ const urls = manifestToScriptUrls(props.manifest, props.base);
98
+ if (urls.length === 0 && !__bfEmptyManifestWarned) {
99
+ __bfEmptyManifestWarned = true;
100
+ console.warn("[barefootjs] BfScripts: manifest is empty — no <script> tags emitted. " + "Run `bf build` to compile components and rebuild the manifest.");
101
+ }
102
+ const tags = urls.map((src) => `<script type="module" src="${src}"></script>`).join("");
103
+ return html`${raw(tags)}`;
104
+ }
105
+ function BfDevReload(props = {}) {
106
+ let endpoint = props.endpoint;
107
+ if (!endpoint) {
108
+ try {
109
+ endpoint = useRequestContext().get(DEV_RELOAD_ENDPOINT_KEY);
110
+ } catch {}
111
+ }
112
+ if (!endpoint)
113
+ return null;
114
+ const ep = JSON.stringify(endpoint);
115
+ const snippet = `(()=>{if(window.__bfDevReload)return;window.__bfDevReload=1;try{var s=sessionStorage.getItem('__bf_devreload_scroll');if(s){sessionStorage.removeItem('__bf_devreload_scroll');var y=parseInt(s,10);if(!isNaN(y)){var restore=function(){window.scrollTo(0,y)};if(document.readyState==='loading'){addEventListener('DOMContentLoaded',restore,{once:true})}else{restore()}}}}catch(e){}var es=new EventSource(${ep});es.addEventListener('reload',function(){try{sessionStorage.setItem('__bf_devreload_scroll',String(window.scrollY))}catch(e){}location.reload()});es.addEventListener('error',function(){})})();`;
116
+ return html`<script>${raw(snippet)}</script>`;
117
+ }
118
+ function barefootDevReload(opts) {
119
+ if (!opts.enabled) {
120
+ return async (_c, next) => next();
121
+ }
122
+ const reloader = createDevReloader();
123
+ const endpoint = opts.endpoint;
124
+ return async (c, next) => {
125
+ c.set(DEV_RELOAD_ENDPOINT_KEY, endpoint);
126
+ if (c.req.method === "GET" && c.req.path === endpoint) {
127
+ return reloader(c);
128
+ }
129
+ await next();
130
+ };
131
+ }
132
+
133
+ // src/scripts.tsx
134
+ import { useRequestContext as useRequestContext2 } from "hono/jsx-renderer";
135
+ import { Fragment } from "hono/jsx";
136
+ import { jsxDEV } from "hono/jsx/jsx-dev-runtime";
137
+ function BfScripts2(props = {}) {
138
+ try {
139
+ const c = useRequestContext2();
140
+ c.set("bfScriptsRendered", true);
141
+ const scripts = c.get("bfCollectedScripts") || [];
142
+ const outputSet = c.get("bfOutputScripts") || new Set;
143
+ const { manifest, base, entryRoots } = props;
144
+ const roots = entryRoots && entryRoots.length > 0 ? new Set([...outputSet, ...entryRoots]) : outputSet;
145
+ if (entryRoots)
146
+ for (const r of entryRoots)
147
+ outputSet.add(r);
148
+ const stubScripts = manifest && base ? collectStubDepScripts(manifest, base, roots, outputSet) : new Map;
149
+ if (stubScripts.size > 0)
150
+ c.set("bfOutputScripts", outputSet);
151
+ const barefootScript = scripts.find((s) => s.src.includes("barefoot.js"));
152
+ const componentScripts = scripts.filter((s) => !s.src.includes("barefoot.js"));
153
+ const finalScripts = [
154
+ ...barefootScript ? [barefootScript] : [],
155
+ ...stubScripts.values(),
156
+ ...componentScripts.reverse()
157
+ ];
158
+ return /* @__PURE__ */ jsxDEV(Fragment, {
159
+ children: finalScripts.map(({ src }) => /* @__PURE__ */ jsxDEV("script", {
160
+ type: "module",
161
+ src
162
+ }, undefined, false, undefined, this))
163
+ }, undefined, false, undefined, this);
164
+ } catch {
165
+ return null;
166
+ }
167
+ }
168
+ function collectStubDepScripts(manifest, base, roots, excluded) {
169
+ const result = new Map;
170
+ const prefix = base.endsWith("/") ? base : base + "/";
171
+ const visited = new Set;
172
+ function visit(name) {
173
+ if (name === "__barefoot__")
174
+ return;
175
+ if (visited.has(name))
176
+ return;
177
+ visited.add(name);
178
+ const entry = manifest[name];
179
+ if (entry?.stubDeps) {
180
+ for (const dep of entry.stubDeps)
181
+ visit(dep);
182
+ }
183
+ if (excluded.has(name))
184
+ return;
185
+ excluded.add(name);
186
+ if (entry?.clientJs) {
187
+ const src = prefix + relPathFromComponentsBase(entry.clientJs);
188
+ result.set(name, { src });
189
+ }
190
+ }
191
+ for (const name of roots)
192
+ visit(name);
193
+ return result;
194
+ }
195
+ export {
196
+ collectStubDepScripts,
197
+ BfScripts2 as BfScripts
198
+ };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Hono test renderer
3
+ *
4
+ * Compiles JSX source with HonoAdapter and renders to HTML via Hono's app.request().
5
+ * Used by adapter-tests conformance runner.
6
+ */
7
+ import type { TemplateAdapter } from '@barefootjs/jsx';
8
+ export interface RenderOptions {
9
+ /** JSX source code */
10
+ source: string;
11
+ /** Template adapter to use */
12
+ adapter: TemplateAdapter;
13
+ /** Props to inject (optional) */
14
+ props?: Record<string, unknown>;
15
+ /** Additional component files (filename → source) */
16
+ components?: Record<string, string>;
17
+ /**
18
+ * Explicit component to render when the source declares multiple
19
+ * exports. When omitted, the first function-valued export in
20
+ * `Object.keys(mod)` iteration order is picked — that order is
21
+ * alphabetical for dynamically imported ES modules in Bun/V8, so
22
+ * relying on declaration order can pick the wrong component
23
+ * (e.g. `PropsReactivityComparison` before `ReactiveProps`).
24
+ */
25
+ componentName?: string;
26
+ }
27
+ export declare function renderHonoComponent(options: RenderOptions): Promise<string>;
28
+ //# sourceMappingURL=test-render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-render.d.ts","sourceRoot":"","sources":["../src/test-render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAQtD,MAAM,WAAW,aAAa;IAC5B,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAA;IACd,8BAA8B;IAC9B,OAAO,EAAE,eAAe,CAAA;IACxB,iCAAiC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA0GjF"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Output HTML comment marker for conditional reconciliation.
3
+ * Same signature as Go template bfComment function.
4
+ */
5
+ export declare function bfComment(key: string): import("hono/utils/html").HtmlEscapedString;
6
+ /**
7
+ * Output opening comment marker for reactive text expressions.
8
+ * Renders <!--bf:slotId-->
9
+ */
10
+ export declare function bfText(slotId: string): import("hono/utils/html").HtmlEscapedString;
11
+ /**
12
+ * Output closing comment marker for reactive text expressions.
13
+ * Renders <!--/-->
14
+ */
15
+ export declare function bfTextEnd(): import("hono/utils/html").HtmlEscapedString;
16
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,+CAEpC;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,+CAEpC;AAED;;;GAGG;AACH,wBAAgB,SAAS,gDAExB"}
package/dist/utils.js ADDED
@@ -0,0 +1,16 @@
1
+ // src/utils.ts
2
+ import { raw } from "hono/html";
3
+ function bfComment(key) {
4
+ return raw(`<!--bf-${key}-->`);
5
+ }
6
+ function bfText(slotId) {
7
+ return raw(`<!--bf:${slotId}-->`);
8
+ }
9
+ function bfTextEnd() {
10
+ return raw("<!--/-->");
11
+ }
12
+ export {
13
+ bfTextEnd,
14
+ bfText,
15
+ bfComment
16
+ };