fibrae 0.2.3 → 0.3.1
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/atom-utils.d.ts +52 -0
- package/dist/atom-utils.js +64 -0
- package/dist/atom-utils.js.map +1 -0
- package/dist/cli/build.d.ts +34 -0
- package/dist/cli/build.js +92 -0
- package/dist/cli/build.js.map +1 -0
- package/dist/cli/cli.d.ts +10 -0
- package/dist/cli/cli.js +43 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/cli/config.d.ts +19 -0
- package/dist/cli/config.js +5 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/html.d.ts +19 -0
- package/dist/cli/html.js +95 -0
- package/dist/cli/html.js.map +1 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/vite-plugin.d.ts +9 -0
- package/dist/cli/vite-plugin.js +143 -0
- package/dist/cli/vite-plugin.js.map +1 -0
- package/dist/components.d.ts +28 -30
- package/dist/components.js +35 -53
- package/dist/components.js.map +1 -1
- package/dist/core.js +7 -10
- package/dist/core.js.map +1 -1
- package/dist/dom.d.ts +25 -6
- package/dist/dom.js +161 -27
- package/dist/dom.js.map +1 -1
- package/dist/fiber-boundary.d.ts +39 -0
- package/dist/fiber-boundary.js +151 -0
- package/dist/fiber-boundary.js.map +1 -0
- package/dist/fiber-commit.d.ts +27 -0
- package/dist/fiber-commit.js +247 -0
- package/dist/fiber-commit.js.map +1 -0
- package/dist/fiber-render.d.ts +9 -9
- package/dist/fiber-render.js +165 -958
- package/dist/fiber-render.js.map +1 -1
- package/dist/fiber-tree.d.ts +77 -0
- package/dist/fiber-tree.js +152 -0
- package/dist/fiber-tree.js.map +1 -0
- package/dist/fiber-update.d.ts +46 -0
- package/dist/fiber-update.js +521 -0
- package/dist/fiber-update.js.map +1 -0
- package/dist/h.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime/index.d.ts +368 -2
- package/dist/live/atom.d.ts +31 -0
- package/dist/live/atom.js +33 -0
- package/dist/live/atom.js.map +1 -0
- package/dist/live/client.d.ts +50 -0
- package/dist/live/client.js +90 -0
- package/dist/live/client.js.map +1 -0
- package/dist/live/codec.d.ts +39 -0
- package/dist/live/codec.js +41 -0
- package/dist/live/codec.js.map +1 -0
- package/dist/live/config.d.ts +13 -0
- package/dist/live/config.js +11 -0
- package/dist/live/config.js.map +1 -0
- package/dist/live/index.d.ts +25 -0
- package/dist/live/index.js +19 -0
- package/dist/live/index.js.map +1 -0
- package/dist/live/server.d.ts +83 -0
- package/dist/live/server.js +106 -0
- package/dist/live/server.js.map +1 -0
- package/dist/live/sse-stream.d.ts +14 -0
- package/dist/live/sse-stream.js +30 -0
- package/dist/live/sse-stream.js.map +1 -0
- package/dist/live/types.d.ts +40 -0
- package/dist/live/types.js +20 -0
- package/dist/live/types.js.map +1 -0
- package/dist/mdx/index.d.ts +125 -0
- package/dist/mdx/index.js +137 -0
- package/dist/mdx/index.js.map +1 -0
- package/dist/mdx/parse.d.ts +42 -0
- package/dist/mdx/parse.js +147 -0
- package/dist/mdx/parse.js.map +1 -0
- package/dist/mdx/render.d.ts +23 -0
- package/dist/mdx/render.js +263 -0
- package/dist/mdx/render.js.map +1 -0
- package/dist/router/Form.d.ts +90 -0
- package/dist/router/Form.js +166 -0
- package/dist/router/Form.js.map +1 -0
- package/dist/router/History.d.ts +4 -9
- package/dist/router/History.js +0 -8
- package/dist/router/History.js.map +1 -1
- package/dist/router/Link.d.ts +27 -28
- package/dist/router/Link.js +50 -119
- package/dist/router/Link.js.map +1 -1
- package/dist/router/Navigator.d.ts +25 -33
- package/dist/router/Navigator.js +41 -149
- package/dist/router/Navigator.js.map +1 -1
- package/dist/router/Route.d.ts +24 -7
- package/dist/router/Route.js +42 -27
- package/dist/router/Route.js.map +1 -1
- package/dist/router/Router.d.ts +27 -19
- package/dist/router/Router.js +112 -120
- package/dist/router/Router.js.map +1 -1
- package/dist/router/RouterBuilder.d.ts +171 -36
- package/dist/router/RouterBuilder.js +101 -39
- package/dist/router/RouterBuilder.js.map +1 -1
- package/dist/router/RouterOutlet.d.ts +1 -18
- package/dist/router/RouterOutlet.js +60 -48
- package/dist/router/RouterOutlet.js.map +1 -1
- package/dist/router/RouterState.d.ts +1 -1
- package/dist/router/index.d.ts +8 -5
- package/dist/router/index.js +6 -3
- package/dist/router/index.js.map +1 -1
- package/dist/router/register.d.ts +37 -0
- package/dist/router/register.js +18 -0
- package/dist/router/register.js.map +1 -0
- package/dist/router/utils.d.ts +36 -0
- package/dist/router/utils.js +48 -0
- package/dist/router/utils.js.map +1 -0
- package/dist/runtime.d.ts +11 -8
- package/dist/runtime.js +20 -2
- package/dist/runtime.js.map +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +15 -29
- package/dist/server.js.map +1 -1
- package/dist/shared.d.ts +61 -62
- package/dist/shared.js +51 -13
- package/dist/shared.js.map +1 -1
- package/dist/tracking.d.ts +4 -3
- package/dist/tracking.js +6 -1
- package/dist/tracking.js.map +1 -1
- package/package.json +45 -7
- package/dist/hydration.d.ts +0 -30
- package/dist/hydration.js +0 -355
- package/dist/hydration.js.map +0 -1
- package/dist/render.d.ts +0 -19
- package/dist/render.js +0 -285
- package/dist/render.js.map +0 -1
- package/dist/scope-utils.d.ts +0 -14
- package/dist/scope-utils.js +0 -29
- package/dist/scope-utils.js.map +0 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component-scoped atom utilities.
|
|
3
|
+
*
|
|
4
|
+
* Thin wrappers that tie atom subscriptions to the component lifecycle
|
|
5
|
+
* via ComponentScope, so cleanup happens automatically on unmount.
|
|
6
|
+
*/
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import * as Scope from "effect/Scope";
|
|
9
|
+
import { Atom, Registry as AtomRegistry } from "@effect-atom/atom";
|
|
10
|
+
import { ComponentScope } from "./shared.js";
|
|
11
|
+
/**
|
|
12
|
+
* Subscribe to an atom for the lifetime of the current component.
|
|
13
|
+
* The subscription is cleaned up automatically when the component unmounts.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* const Counter = () =>
|
|
18
|
+
* Effect.gen(function* () {
|
|
19
|
+
* yield* subscribeAtom(countAtom, (value) => {
|
|
20
|
+
* console.log("count changed:", value);
|
|
21
|
+
* });
|
|
22
|
+
* const count = yield* Atom.get(countAtom);
|
|
23
|
+
* return <div>Count: {count}</div>;
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare const subscribeAtom: <A>(atom: Atom.Atom<A>, callback: (value: A) => void) => Effect.Effect<void, never, AtomRegistry.AtomRegistry | ComponentScope>;
|
|
28
|
+
/**
|
|
29
|
+
* Run an Effect after the component mounts, scoped to the component lifetime.
|
|
30
|
+
* The forked fiber is interrupted when the component unmounts.
|
|
31
|
+
*
|
|
32
|
+
* Useful for imperative setup (DOM manipulation, external library init)
|
|
33
|
+
* that needs to wait for the DOM to be ready.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```tsx
|
|
37
|
+
* const Editor = () =>
|
|
38
|
+
* Effect.gen(function* () {
|
|
39
|
+
* const ref = { current: null as HTMLDivElement | null };
|
|
40
|
+
*
|
|
41
|
+
* yield* mountAtom(
|
|
42
|
+
* Effect.gen(function* () {
|
|
43
|
+
* const editor = monaco.create(ref.current!);
|
|
44
|
+
* yield* Effect.addFinalizer(() => Effect.sync(() => editor.dispose()));
|
|
45
|
+
* }),
|
|
46
|
+
* );
|
|
47
|
+
*
|
|
48
|
+
* return <div ref={el => ref.current = el} />;
|
|
49
|
+
* });
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare const mountAtom: <A, E>(effect: Effect.Effect<A, E, Scope.Scope>) => Effect.Effect<void, never, ComponentScope>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component-scoped atom utilities.
|
|
3
|
+
*
|
|
4
|
+
* Thin wrappers that tie atom subscriptions to the component lifecycle
|
|
5
|
+
* via ComponentScope, so cleanup happens automatically on unmount.
|
|
6
|
+
*/
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import * as Scope from "effect/Scope";
|
|
9
|
+
import { Atom, Registry as AtomRegistry } from "@effect-atom/atom";
|
|
10
|
+
import { ComponentScope } from "./shared.js";
|
|
11
|
+
/**
|
|
12
|
+
* Subscribe to an atom for the lifetime of the current component.
|
|
13
|
+
* The subscription is cleaned up automatically when the component unmounts.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* const Counter = () =>
|
|
18
|
+
* Effect.gen(function* () {
|
|
19
|
+
* yield* subscribeAtom(countAtom, (value) => {
|
|
20
|
+
* console.log("count changed:", value);
|
|
21
|
+
* });
|
|
22
|
+
* const count = yield* Atom.get(countAtom);
|
|
23
|
+
* return <div>Count: {count}</div>;
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export const subscribeAtom = (atom, callback) => Effect.gen(function* () {
|
|
28
|
+
const { scope } = yield* ComponentScope;
|
|
29
|
+
const registry = yield* AtomRegistry.AtomRegistry;
|
|
30
|
+
const unsubscribe = registry.subscribe(atom, callback);
|
|
31
|
+
yield* Scope.addFinalizer(scope, Effect.sync(unsubscribe));
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* Run an Effect after the component mounts, scoped to the component lifetime.
|
|
35
|
+
* The forked fiber is interrupted when the component unmounts.
|
|
36
|
+
*
|
|
37
|
+
* Useful for imperative setup (DOM manipulation, external library init)
|
|
38
|
+
* that needs to wait for the DOM to be ready.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* const Editor = () =>
|
|
43
|
+
* Effect.gen(function* () {
|
|
44
|
+
* const ref = { current: null as HTMLDivElement | null };
|
|
45
|
+
*
|
|
46
|
+
* yield* mountAtom(
|
|
47
|
+
* Effect.gen(function* () {
|
|
48
|
+
* const editor = monaco.create(ref.current!);
|
|
49
|
+
* yield* Effect.addFinalizer(() => Effect.sync(() => editor.dispose()));
|
|
50
|
+
* }),
|
|
51
|
+
* );
|
|
52
|
+
*
|
|
53
|
+
* return <div ref={el => ref.current = el} />;
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export const mountAtom = (effect) => Effect.gen(function* () {
|
|
58
|
+
const { scope, mounted } = yield* ComponentScope;
|
|
59
|
+
yield* Effect.gen(function* () {
|
|
60
|
+
yield* mounted;
|
|
61
|
+
yield* effect;
|
|
62
|
+
}).pipe(Effect.forkScoped, Scope.extend(scope));
|
|
63
|
+
});
|
|
64
|
+
//# sourceMappingURL=atom-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atom-utils.js","sourceRoot":"","sources":["../src/atom-utils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,IAAkB,EAClB,QAA4B,EAC4C,EAAE,CAC1E,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC;IACxC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC;IAClD,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACvD,KAAK,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;AAC7D,CAAC,CAAC,CAAC;AAEL;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,MAAwC,EACI,EAAE,CAC9C,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC;IACjD,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACzB,KAAK,CAAC,CAAC,OAAO,CAAC;QACf,KAAK,CAAC,CAAC,MAAM,CAAC;IAChB,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSG build pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates: Vite client build → route discovery → pre-render → write HTML.
|
|
5
|
+
*/
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Layer from "effect/Layer";
|
|
8
|
+
import { FileSystem } from "@effect/platform";
|
|
9
|
+
import { Path } from "@effect/platform";
|
|
10
|
+
import { Router, RouterHandlers } from "../router/index.js";
|
|
11
|
+
import type { HeadData } from "../router/index.js";
|
|
12
|
+
import type { VElement } from "../shared.js";
|
|
13
|
+
export interface BuildOptions {
|
|
14
|
+
/** The fibrae router instance */
|
|
15
|
+
readonly router: Router.Router;
|
|
16
|
+
/** Layer providing RouterHandlers */
|
|
17
|
+
readonly handlersLayer: Layer.Layer<RouterHandlers>;
|
|
18
|
+
/** Wraps each route's rendered element in the app shell */
|
|
19
|
+
readonly appShell: (element: VElement) => VElement;
|
|
20
|
+
/** Output directory */
|
|
21
|
+
readonly outDir: string;
|
|
22
|
+
/** Base path prefix for routes (e.g. "/app") */
|
|
23
|
+
readonly basePath?: string;
|
|
24
|
+
/** Path to client JS bundle (relative to site root) */
|
|
25
|
+
readonly clientScript?: string;
|
|
26
|
+
/** Default page title */
|
|
27
|
+
readonly title?: string;
|
|
28
|
+
/** Global head tags injected into every page */
|
|
29
|
+
readonly headTags?: HeadData;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Pre-render all routes marked with `prerender: true` to static HTML files.
|
|
33
|
+
*/
|
|
34
|
+
export declare const build: (options: BuildOptions) => Effect.Effect<void, unknown, FileSystem.FileSystem | Path.Path>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSG build pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates: Vite client build → route discovery → pre-render → write HTML.
|
|
5
|
+
*/
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Layer from "effect/Layer";
|
|
8
|
+
import { FileSystem } from "@effect/platform";
|
|
9
|
+
import { Path } from "@effect/platform";
|
|
10
|
+
import { renderToStringWith, SSRAtomRegistryLayer } from "../server.js";
|
|
11
|
+
import { Router, RouterHandlers, getPrerenderRoutes } from "../router/index.js";
|
|
12
|
+
import { buildPage } from "./html.js";
|
|
13
|
+
/**
|
|
14
|
+
* Render a single route to an HTML string.
|
|
15
|
+
*/
|
|
16
|
+
const renderRoute = (config) => Effect.gen(function* () {
|
|
17
|
+
const serverLayer = Router.serverLayer({
|
|
18
|
+
router: config.router,
|
|
19
|
+
pathname: config.pathname,
|
|
20
|
+
search: "",
|
|
21
|
+
basePath: config.basePath,
|
|
22
|
+
});
|
|
23
|
+
const fullLayer = Layer.provideMerge(serverLayer, Layer.merge(config.handlersLayer, SSRAtomRegistryLayer));
|
|
24
|
+
const { html, dehydratedState, head } = yield* Effect.gen(function* () {
|
|
25
|
+
const { element, head: routeHead } = yield* Router.CurrentRouteElement;
|
|
26
|
+
const renderResult = yield* renderToStringWith(config.appShell(element));
|
|
27
|
+
return { ...renderResult, head: routeHead };
|
|
28
|
+
}).pipe(Effect.provide(fullLayer));
|
|
29
|
+
return yield* buildPage({
|
|
30
|
+
html,
|
|
31
|
+
dehydratedState: dehydratedState,
|
|
32
|
+
clientScript: config.clientScript,
|
|
33
|
+
title: config.title,
|
|
34
|
+
head,
|
|
35
|
+
headTags: config.headTags,
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Compute all (pathname, handler) pairs from prerender routes.
|
|
40
|
+
*/
|
|
41
|
+
const expandRoutes = (prerenderRoutes, basePath) => Effect.all(prerenderRoutes.flatMap(({ handler, paramSets }) => paramSets.map((params) => handler.route
|
|
42
|
+
.interpolate(params)
|
|
43
|
+
.pipe(Effect.map((pathname) => ({ pathname: basePath + pathname, handler }))))));
|
|
44
|
+
/**
|
|
45
|
+
* Write an HTML string to the appropriate file path under outDir.
|
|
46
|
+
* Creates directories as needed.
|
|
47
|
+
*
|
|
48
|
+
* / → outDir/index.html
|
|
49
|
+
* /about → outDir/about/index.html
|
|
50
|
+
* /posts/1 → outDir/posts/1/index.html
|
|
51
|
+
*/
|
|
52
|
+
const writePageFile = (outDir, pathname, html) => Effect.gen(function* () {
|
|
53
|
+
const fs = yield* FileSystem.FileSystem;
|
|
54
|
+
const path = yield* Path.Path;
|
|
55
|
+
const filePath = pathname === "/" || pathname === ""
|
|
56
|
+
? path.join(outDir, "index.html")
|
|
57
|
+
: path.join(outDir, pathname, "index.html");
|
|
58
|
+
yield* fs.makeDirectory(path.dirname(filePath), { recursive: true });
|
|
59
|
+
yield* fs.writeFileString(filePath, html);
|
|
60
|
+
});
|
|
61
|
+
/**
|
|
62
|
+
* Pre-render all routes marked with `prerender: true` to static HTML files.
|
|
63
|
+
*/
|
|
64
|
+
export const build = (options) => Effect.gen(function* () {
|
|
65
|
+
const { router, handlersLayer, appShell, outDir, basePath = "", clientScript, title, headTags, } = options;
|
|
66
|
+
// Resolve handlers to get prerender routes
|
|
67
|
+
const handlers = yield* Effect.provide(Effect.flatMap(RouterHandlers, (h) => getPrerenderRoutes(h)), handlersLayer);
|
|
68
|
+
const routes = yield* expandRoutes(handlers, basePath);
|
|
69
|
+
if (routes.length === 0) {
|
|
70
|
+
yield* Effect.log("No prerender routes found.");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
yield* Effect.log(`Pre-rendering ${routes.length} page(s)...`);
|
|
74
|
+
// Render all routes
|
|
75
|
+
const pages = yield* Effect.all(routes.map(({ pathname }) => renderRoute({
|
|
76
|
+
router,
|
|
77
|
+
handlersLayer,
|
|
78
|
+
appShell,
|
|
79
|
+
pathname,
|
|
80
|
+
basePath,
|
|
81
|
+
clientScript,
|
|
82
|
+
title,
|
|
83
|
+
headTags,
|
|
84
|
+
}).pipe(Effect.map((html) => ({ pathname, html })))));
|
|
85
|
+
// Write all pages to disk
|
|
86
|
+
yield* Effect.forEach(pages, ({ pathname, html }) => Effect.gen(function* () {
|
|
87
|
+
yield* writePageFile(outDir, pathname, html);
|
|
88
|
+
yield* Effect.log(` ${pathname} → ${outDir}${pathname === "/" ? "/index.html" : `${pathname}/index.html`}`);
|
|
89
|
+
}), { discard: true });
|
|
90
|
+
yield* Effect.log(`Done. ${pages.length} page(s) written to ${outDir}/`);
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/cli/build.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* fibrae CLI — static site generation for fibrae apps.
|
|
4
|
+
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* fibrae build Pre-render routes and build client bundle
|
|
7
|
+
* fibrae dev Start Vite dev server with on-demand SSR
|
|
8
|
+
* fibrae preview Serve the built output
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
package/dist/cli/cli.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* fibrae CLI — static site generation for fibrae apps.
|
|
4
|
+
*
|
|
5
|
+
* Commands:
|
|
6
|
+
* fibrae build Pre-render routes and build client bundle
|
|
7
|
+
* fibrae dev Start Vite dev server with on-demand SSR
|
|
8
|
+
* fibrae preview Serve the built output
|
|
9
|
+
*/
|
|
10
|
+
const [command] = process.argv.slice(2);
|
|
11
|
+
const commands = {
|
|
12
|
+
async build() {
|
|
13
|
+
const { build } = await import("vite");
|
|
14
|
+
await build();
|
|
15
|
+
},
|
|
16
|
+
async dev() {
|
|
17
|
+
const { createServer } = await import("vite");
|
|
18
|
+
const server = await createServer({ server: { open: true } });
|
|
19
|
+
await server.listen();
|
|
20
|
+
server.printUrls();
|
|
21
|
+
},
|
|
22
|
+
async preview() {
|
|
23
|
+
const { preview } = await import("vite");
|
|
24
|
+
const server = await preview();
|
|
25
|
+
server.printUrls();
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
const run = commands[command ?? ""];
|
|
29
|
+
if (!run) {
|
|
30
|
+
console.log(`Usage: fibrae <command>
|
|
31
|
+
|
|
32
|
+
Commands:
|
|
33
|
+
build Pre-render routes and build client bundle
|
|
34
|
+
dev Start Vite dev server with on-demand SSR
|
|
35
|
+
preview Serve the built output`);
|
|
36
|
+
process.exit(command ? 1 : 0);
|
|
37
|
+
}
|
|
38
|
+
run().catch((err) => {
|
|
39
|
+
console.error(err);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
});
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli/cli.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AAEH,MAAM,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAExC,MAAM,QAAQ,GAAwC;IACpD,KAAK,CAAC,KAAK;QACT,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,KAAK,EAAE,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QAC9D,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;QACtB,MAAM,CAAC,SAAS,EAAE,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,OAAO,EAAE,CAAC;QAC/B,MAAM,CAAC,SAAS,EAAE,CAAC;IACrB,CAAC;CACF,CAAC;AAEF,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;AAEpC,IAAI,CAAC,GAAG,EAAE,CAAC;IACT,OAAO,CAAC,GAAG,CAAC;;;;;kCAKoB,CAAC,CAAC;IAClC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAClB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration types for fibrae-cli.
|
|
3
|
+
*/
|
|
4
|
+
import type { HeadData } from "../router/RouterBuilder.js";
|
|
5
|
+
export interface FibraeConfig {
|
|
6
|
+
/** Module path that exports { router, handlers, App } */
|
|
7
|
+
readonly entry: string;
|
|
8
|
+
/** Client hydration entry point */
|
|
9
|
+
readonly client: string;
|
|
10
|
+
/** Output directory (default: "dist") */
|
|
11
|
+
readonly outDir?: string;
|
|
12
|
+
/** Base path prefix for routes */
|
|
13
|
+
readonly basePath?: string;
|
|
14
|
+
/** Default page title */
|
|
15
|
+
readonly title?: string;
|
|
16
|
+
/** Global head tags injected into every page (fonts, analytics, meta, etc.) */
|
|
17
|
+
readonly headTags?: HeadData;
|
|
18
|
+
}
|
|
19
|
+
export declare const defineConfig: (config: FibraeConfig) => FibraeConfig;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAmBH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAoB,EAAgB,EAAE,CAAC,MAAM,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML page template for static site generation.
|
|
3
|
+
*
|
|
4
|
+
* Uses fibrae JSX to generate full HTML documents wrapping
|
|
5
|
+
* pre-rendered content with dehydrated state and client scripts.
|
|
6
|
+
*/
|
|
7
|
+
import type { HeadData } from "../router/RouterBuilder.js";
|
|
8
|
+
import * as Effect from "effect/Effect";
|
|
9
|
+
import * as Option from "effect/Option";
|
|
10
|
+
export interface PageOptions {
|
|
11
|
+
readonly html: string;
|
|
12
|
+
readonly dehydratedState: ReadonlyArray<unknown>;
|
|
13
|
+
readonly clientScript?: string;
|
|
14
|
+
readonly title?: string;
|
|
15
|
+
readonly head: Option.Option<HeadData>;
|
|
16
|
+
/** Global head tags from config, merged with per-route head */
|
|
17
|
+
readonly headTags?: HeadData;
|
|
18
|
+
}
|
|
19
|
+
export declare const buildPage: (options: PageOptions) => Effect.Effect<string, unknown, never>;
|
package/dist/cli/html.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML page template for static site generation.
|
|
3
|
+
*
|
|
4
|
+
* Uses fibrae JSX to generate full HTML documents wrapping
|
|
5
|
+
* pre-rendered content with dehydrated state and client scripts.
|
|
6
|
+
*/
|
|
7
|
+
// eslint-disable-next-line no-unused-vars -- jsx is used by the JSX transform (jsxFactory)
|
|
8
|
+
import { jsx } from "../jsx-runtime/index.js";
|
|
9
|
+
import { h } from "../h.js";
|
|
10
|
+
import { renderToString } from "../server.js";
|
|
11
|
+
import * as Effect from "effect/Effect";
|
|
12
|
+
import * as Option from "effect/Option";
|
|
13
|
+
import * as Array from "effect/Array";
|
|
14
|
+
const metaToElement = (meta) => {
|
|
15
|
+
if ("title" in meta)
|
|
16
|
+
return Option.none();
|
|
17
|
+
if ("charset" in meta)
|
|
18
|
+
return Option.some(jsx("meta", { charset: meta.charset }));
|
|
19
|
+
if ("script:ld+json" in meta)
|
|
20
|
+
return Option.some(jsx("script", { type: "application/ld+json", dangerouslySetInnerHTML: JSON.stringify(meta["script:ld+json"]) }));
|
|
21
|
+
if ("name" in meta)
|
|
22
|
+
return Option.some(jsx("meta", { name: meta.name, content: meta.content }));
|
|
23
|
+
if ("property" in meta)
|
|
24
|
+
return Option.some(jsx("meta", { property: meta.property, content: meta.content }));
|
|
25
|
+
if ("httpEquiv" in meta)
|
|
26
|
+
return Option.some(jsx("meta", { "http-equiv": meta.httpEquiv, content: meta.content }));
|
|
27
|
+
if ("tagName" in meta) {
|
|
28
|
+
const { tagName, ...attrs } = meta;
|
|
29
|
+
// Dynamic tag name -- JSX requires literal tags, so use h() directly
|
|
30
|
+
return Option.some(h(tagName, attrs));
|
|
31
|
+
}
|
|
32
|
+
return Option.none();
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Get the dedup key for a meta descriptor.
|
|
36
|
+
* Per TanStack Router pattern: meta tags with the same name/property are
|
|
37
|
+
* deduplicated, with per-route tags winning over global tags.
|
|
38
|
+
*/
|
|
39
|
+
const metaKey = (meta) => {
|
|
40
|
+
if ("name" in meta)
|
|
41
|
+
return `name:${meta.name}`;
|
|
42
|
+
if ("property" in meta)
|
|
43
|
+
return `property:${meta.property}`;
|
|
44
|
+
if ("httpEquiv" in meta)
|
|
45
|
+
return `httpEquiv:${meta.httpEquiv}`;
|
|
46
|
+
if ("charset" in meta)
|
|
47
|
+
return "charset";
|
|
48
|
+
return undefined;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Merge meta arrays with deduplication. Per-route entries override globals
|
|
52
|
+
* when they share the same name/property/httpEquiv key.
|
|
53
|
+
*/
|
|
54
|
+
const dedupMeta = (global, perRoute) => {
|
|
55
|
+
const routeKeys = new Set(perRoute.map(metaKey).filter(Boolean));
|
|
56
|
+
const filtered = global.filter((m) => {
|
|
57
|
+
const key = metaKey(m);
|
|
58
|
+
return key === undefined || !routeKeys.has(key);
|
|
59
|
+
});
|
|
60
|
+
return [...filtered, ...perRoute];
|
|
61
|
+
};
|
|
62
|
+
const buildHeadChildren = (title, head, headTags) => {
|
|
63
|
+
const headData = Option.getOrUndefined(head);
|
|
64
|
+
// Per-route title wins, then global headTags title, then config title
|
|
65
|
+
const pageTitle = headData?.title ?? headTags?.title ?? title;
|
|
66
|
+
// Merge global headTags with per-route head.
|
|
67
|
+
// Meta tags are deduplicated by name/property (per-route wins).
|
|
68
|
+
// Links and scripts are concatenated (global first, then per-route).
|
|
69
|
+
const allMeta = dedupMeta(headTags?.meta ?? [], headData?.meta ?? []);
|
|
70
|
+
const allLinks = [...(headTags?.links ?? []), ...(headData?.links ?? [])];
|
|
71
|
+
const allScripts = [...(headTags?.scripts ?? []), ...(headData?.scripts ?? [])];
|
|
72
|
+
return [
|
|
73
|
+
jsx("meta", { charset: "UTF-8" }),
|
|
74
|
+
jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0" }),
|
|
75
|
+
...Option.match(Option.fromNullable(pageTitle), {
|
|
76
|
+
onNone: () => [],
|
|
77
|
+
onSome: (t) => [jsx("title", null, t)],
|
|
78
|
+
}),
|
|
79
|
+
...Array.filterMap(allMeta, metaToElement),
|
|
80
|
+
...allLinks.map((attrs) => jsx("link", { ...attrs })),
|
|
81
|
+
...allScripts.flatMap((script) => script.src
|
|
82
|
+
? [jsx("script", { type: script.type, src: script.src })]
|
|
83
|
+
: script.content
|
|
84
|
+
? [jsx("script", { type: script.type, dangerouslySetInnerHTML: script.content })]
|
|
85
|
+
: []),
|
|
86
|
+
];
|
|
87
|
+
};
|
|
88
|
+
const PageShell = (props) => (jsx("html", { lang: "en" },
|
|
89
|
+
jsx("head", null, buildHeadChildren(props.title, props.head, props.headTags)),
|
|
90
|
+
jsx("body", null,
|
|
91
|
+
jsx("div", { id: "root", dangerouslySetInnerHTML: props.html }),
|
|
92
|
+
jsx("script", { type: "application/json", id: "__fibrae-state__", dangerouslySetInnerHTML: JSON.stringify(props.dehydratedState) }),
|
|
93
|
+
props.clientScript ? jsx("script", { type: "module", src: props.clientScript }) : null)));
|
|
94
|
+
export const buildPage = (options) => renderToString(PageShell(options)).pipe(Effect.map(({ html }) => `<!DOCTYPE html>\n${html}`));
|
|
95
|
+
//# sourceMappingURL=html.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/cli/html.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { buildPage } from "./html.js";
|
|
2
|
+
export type { PageOptions } from "./html.js";
|
|
3
|
+
export { build } from "./build.js";
|
|
4
|
+
export type { BuildOptions } from "./build.js";
|
|
5
|
+
export { defineConfig } from "./config.js";
|
|
6
|
+
export type { FibraeConfig } from "./config.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite plugin for fibrae static site generation.
|
|
3
|
+
*
|
|
4
|
+
* Hooks into Vite's build pipeline to pre-render routes after the client build.
|
|
5
|
+
* In dev mode, provides on-demand SSR middleware.
|
|
6
|
+
*/
|
|
7
|
+
import type { Plugin } from "vite";
|
|
8
|
+
import type { FibraeConfig } from "./config.js";
|
|
9
|
+
export declare const fibrae: (config: FibraeConfig) => Plugin<any>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Option from "effect/Option";
|
|
3
|
+
import * as Layer from "effect/Layer";
|
|
4
|
+
import { NodeContext } from "@effect/platform-node";
|
|
5
|
+
import { Router, RouterHandlers } from "../router/index.js";
|
|
6
|
+
import { renderToStringWith, SSRAtomRegistryLayer } from "../server.js";
|
|
7
|
+
import { buildPage } from "./html.js";
|
|
8
|
+
/**
|
|
9
|
+
* Render a single page for the dev server.
|
|
10
|
+
*/
|
|
11
|
+
const renderDevPage = (opts) => Effect.gen(function* () {
|
|
12
|
+
const serverLayer = Router.serverLayer({
|
|
13
|
+
router: opts.router,
|
|
14
|
+
pathname: opts.pathname,
|
|
15
|
+
search: "",
|
|
16
|
+
basePath: opts.basePath,
|
|
17
|
+
});
|
|
18
|
+
const fullLayer = Layer.provideMerge(serverLayer, Layer.merge(opts.handlersLayer, SSRAtomRegistryLayer));
|
|
19
|
+
const { html, dehydratedState, head } = yield* Effect.gen(function* () {
|
|
20
|
+
const { head: routeHead } = yield* Router.CurrentRouteElement;
|
|
21
|
+
const renderResult = opts.App
|
|
22
|
+
? yield* renderToStringWith(opts.App())
|
|
23
|
+
: yield* Effect.gen(function* () {
|
|
24
|
+
const { element } = yield* Router.CurrentRouteElement;
|
|
25
|
+
const app = opts.appShell ? opts.appShell(element) : element;
|
|
26
|
+
return yield* renderToStringWith(app);
|
|
27
|
+
});
|
|
28
|
+
return { ...renderResult, head: routeHead };
|
|
29
|
+
}).pipe(Effect.provide(fullLayer));
|
|
30
|
+
return yield* buildPage({
|
|
31
|
+
html,
|
|
32
|
+
dehydratedState: dehydratedState,
|
|
33
|
+
clientScript: opts.clientScript,
|
|
34
|
+
title: opts.title,
|
|
35
|
+
head,
|
|
36
|
+
headTags: opts.headTags,
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* Walk the Vite module graph from an entry and collect CSS module URLs.
|
|
41
|
+
* Injects these as <style> tags in the SSR HTML to prevent FOUC.
|
|
42
|
+
*/
|
|
43
|
+
const collectCss = async (server, entryUrl) => {
|
|
44
|
+
// Warm the module graph so CSS deps are discovered
|
|
45
|
+
await server.transformRequest(entryUrl);
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
const cssUrls = [];
|
|
48
|
+
const walk = (mod) => {
|
|
49
|
+
if (!mod?.id || seen.has(mod.id))
|
|
50
|
+
return;
|
|
51
|
+
seen.add(mod.id);
|
|
52
|
+
if (mod.id.endsWith(".css")) {
|
|
53
|
+
cssUrls.push(mod.url);
|
|
54
|
+
}
|
|
55
|
+
for (const dep of mod.importedModules) {
|
|
56
|
+
walk(dep);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
walk(await server.moduleGraph.getModuleByUrl(entryUrl));
|
|
60
|
+
return cssUrls;
|
|
61
|
+
};
|
|
62
|
+
export const fibrae = (config) => {
|
|
63
|
+
let _resolvedConfig;
|
|
64
|
+
return {
|
|
65
|
+
name: "fibrae-ssg",
|
|
66
|
+
configResolved(resolved) {
|
|
67
|
+
_resolvedConfig = resolved;
|
|
68
|
+
},
|
|
69
|
+
configureServer(server) {
|
|
70
|
+
server.middlewares.use(async (req, res, next) => {
|
|
71
|
+
const url = req.url;
|
|
72
|
+
if (!url || url.startsWith("/@") || url.includes(".")) {
|
|
73
|
+
return next();
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const appModule = await server.ssrLoadModule(config.entry);
|
|
77
|
+
const { router, handlersLayer, appShell, App } = appModule;
|
|
78
|
+
if (!router || !handlersLayer) {
|
|
79
|
+
return next();
|
|
80
|
+
}
|
|
81
|
+
// Only SSR routes the router knows about; pass everything else through
|
|
82
|
+
if (Option.isNone(router.matchRoute(url))) {
|
|
83
|
+
return next();
|
|
84
|
+
}
|
|
85
|
+
const rawHtml = await Effect.runPromise(renderDevPage({
|
|
86
|
+
router,
|
|
87
|
+
handlersLayer,
|
|
88
|
+
App,
|
|
89
|
+
appShell,
|
|
90
|
+
pathname: url,
|
|
91
|
+
basePath: config.basePath ?? "",
|
|
92
|
+
clientScript: config.client,
|
|
93
|
+
title: config.title,
|
|
94
|
+
headTags: config.headTags,
|
|
95
|
+
}));
|
|
96
|
+
// Collect CSS from client entry's module graph and inject into <head>
|
|
97
|
+
const cssUrls = await collectCss(server, config.client);
|
|
98
|
+
const cssTags = cssUrls.map((u) => `<link rel="stylesheet" href="${u}">`).join("\n");
|
|
99
|
+
const withCss = cssTags ? rawHtml.replace("</head>", `${cssTags}\n</head>`) : rawHtml;
|
|
100
|
+
// Let Vite inject HMR client
|
|
101
|
+
const result = await server.transformIndexHtml(url, withCss);
|
|
102
|
+
res.setHeader("Content-Type", "text/html");
|
|
103
|
+
res.end(result);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
next();
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
async closeBundle() {
|
|
111
|
+
if (_resolvedConfig.command !== "build")
|
|
112
|
+
return;
|
|
113
|
+
const outDir = config.outDir ?? _resolvedConfig.build.outDir ?? "dist";
|
|
114
|
+
try {
|
|
115
|
+
const entryPath = new URL(config.entry, `file://${process.cwd()}/`).pathname;
|
|
116
|
+
const appModule = await import(entryPath);
|
|
117
|
+
const { router, handlersLayer, appShell } = appModule;
|
|
118
|
+
if (!router || !handlersLayer) {
|
|
119
|
+
console.warn("[fibrae-ssg] Entry module missing router or handlersLayer exports. Skipping SSG.");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const { build: ssgBuild } = await import("./build.js");
|
|
123
|
+
const clientScript = config.client
|
|
124
|
+
? `/${config.client.replace(/\.tsx?$/, ".js")}`
|
|
125
|
+
: undefined;
|
|
126
|
+
await Effect.runPromise(ssgBuild({
|
|
127
|
+
router,
|
|
128
|
+
handlersLayer,
|
|
129
|
+
appShell: appShell ?? ((el) => el),
|
|
130
|
+
outDir,
|
|
131
|
+
basePath: config.basePath ?? "",
|
|
132
|
+
clientScript,
|
|
133
|
+
title: config.title,
|
|
134
|
+
headTags: config.headTags,
|
|
135
|
+
}).pipe(Effect.provide(NodeContext.layer)));
|
|
136
|
+
}
|
|
137
|
+
catch (e) {
|
|
138
|
+
console.error("[fibrae-ssg] Pre-render failed:", e);
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
//# sourceMappingURL=vite-plugin.js.map
|