@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.
- package/dist/adapter/hono-adapter.d.ts +141 -0
- package/dist/adapter/hono-adapter.d.ts.map +1 -0
- package/dist/adapter/index.d.ts +6 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/index.js +632 -0
- package/dist/app.d.ts +131 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +139 -0
- package/dist/async.d.ts +15 -0
- package/dist/async.d.ts.map +1 -0
- package/dist/async.js +12 -0
- package/dist/build.d.ts +65 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +785 -0
- package/dist/client-shim.d.ts +59 -0
- package/dist/client-shim.d.ts.map +1 -0
- package/dist/client-shim.js +90 -0
- package/dist/dev-worker.d.ts +25 -0
- package/dist/dev-worker.d.ts.map +1 -0
- package/dist/dev-worker.js +65 -0
- package/dist/dev.d.ts +36 -0
- package/dist/dev.d.ts.map +1 -0
- package/dist/dev.js +418 -0
- package/dist/dialog-context.d.ts +13 -0
- package/dist/dialog-context.d.ts.map +1 -0
- package/dist/dialog-context.js +10 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +632 -0
- package/dist/jsx/jsx-dev-runtime/index.d.ts +9 -0
- package/dist/jsx/jsx-dev-runtime/index.d.ts.map +1 -0
- package/dist/jsx/jsx-dev-runtime/index.js +6 -0
- package/dist/jsx/jsx-runtime/index.d.ts +32 -0
- package/dist/jsx/jsx-runtime/index.d.ts.map +1 -0
- package/dist/jsx/jsx-runtime/index.js +10 -0
- package/dist/portal-ssr.d.ts +22 -0
- package/dist/portal-ssr.d.ts.map +1 -0
- package/dist/portal-ssr.js +73 -0
- package/dist/portals.d.ts +26 -0
- package/dist/portals.d.ts.map +1 -0
- package/dist/portals.js +41 -0
- package/dist/preload.d.ts +56 -0
- package/dist/preload.d.ts.map +1 -0
- package/dist/preload.js +51 -0
- package/dist/scripts.d.ts +80 -0
- package/dist/scripts.d.ts.map +1 -0
- package/dist/scripts.js +198 -0
- package/dist/test-render.d.ts +28 -0
- package/dist/test-render.d.ts.map +1 -0
- package/dist/utils.d.ts +16 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +16 -0
- package/package.json +116 -0
- package/src/__tests__/async.test.tsx +106 -0
- package/src/__tests__/bfscripts-entry-roots.test.tsx +135 -0
- package/src/__tests__/build.test.ts +299 -0
- package/src/__tests__/dev.test.tsx +123 -0
- package/src/__tests__/hydration-props-type.test.ts +141 -0
- package/src/__tests__/manifest-scripts.test.ts +87 -0
- package/src/__tests__/scaffold.test.ts +209 -0
- package/src/__tests__/ssr-context-bridge.test.ts +110 -0
- package/src/__tests__/string-literal-css-var-prop.test.ts +84 -0
- package/src/__tests__/stub-deps-scripts.test.ts +183 -0
- package/src/adapter/hono-adapter.ts +1114 -0
- package/src/adapter/index.ts +6 -0
- package/src/app.ts +220 -0
- package/src/async.tsx +55 -0
- package/src/build.ts +230 -0
- package/src/client-shim.ts +164 -0
- package/src/dev-worker.ts +93 -0
- package/src/dev.tsx +146 -0
- package/src/dialog-context.tsx +44 -0
- package/src/index.ts +26 -0
- package/src/jsx/jsx-dev-runtime/index.ts +9 -0
- package/src/jsx/jsx-runtime/index.ts +40 -0
- package/src/portal-ssr.tsx +92 -0
- package/src/portals.tsx +98 -0
- package/src/preload.tsx +166 -0
- package/src/scripts.tsx +220 -0
- package/src/test-render.ts +143 -0
- package/src/utils.ts +26 -0
|
@@ -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"}
|
package/dist/portals.js
ADDED
|
@@ -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"}
|
package/dist/preload.js
ADDED
|
@@ -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"}
|
package/dist/scripts.js
ADDED
|
@@ -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"}
|
package/dist/utils.d.ts
ADDED
|
@@ -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
|
+
};
|