Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
],
"exports": {
".": "./src/index.ts",
"./index.client": "./src/index.client.ts",
"./head": "./src/react/components/Head.tsx",
"./router": "./src/react/router/index.tsx",
"./context": "./src/react/context/index.tsx",
Expand Down Expand Up @@ -144,6 +145,7 @@
"./server": "./src/server/index.ts"
},
"imports": {
"veryfront/index.client": "./src/index.client.ts",
"veryfront/head": "./src/react/runtime/core.ts",
"veryfront/router": "./src/react/runtime/core.ts",
"veryfront/context": "./src/react/runtime/core.ts",
Expand Down
4 changes: 4 additions & 0 deletions scripts/build/browser-safe-exports.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
export const BROWSER_SAFE_EXPORTS = [
// Client/SSR-safe mirror of the root barrel (server bootstrap surface removed).
// The import rewriter redirects `veryfront` here for browser/ssr; it must ship
// in the npm package (built to esm/src/index.client.js) or that redirect 404s.
"./index.client",
"./head",
"./router",
"./context",
Expand Down
70 changes: 70 additions & 0 deletions src/index.client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* Client/SSR-safe mirror of the `veryfront` root barrel ({@link file://./index.ts}).
*
* The root barrel re-exports the server bootstrap surface (`createHandler`,
* `startServer`, `toNodeHandler`) from `#veryfront/server`. Because the browser
* and SSR pipelines transform modules per-file (no cross-module tree-shaking),
* an ESM re-export eagerly loads its source module — so pulling the root barrel
* into a client chunk drags the entire server graph in, including
* `src/server/production-server.ts`, which has module top-level `await` and
* cannot be transformed to the es2020 browser target (→ HTTP 500 on that chunk,
* which aborts hydration).
*
* A client-reachable module doing a *used* value import from the barrel (e.g.
* `import { getEnv } from "veryfront"`) is not dead-stripped, so it keeps the
* barrel — and the leak. This barrel exposes exactly the same browser-safe
* surface minus the server bootstrap functions, which no client/SSR page code
* ever legitimately calls. The import rewriter redirects `veryfront` to this
* module for the `browser` and `ssr` targets (see
* `src/transforms/import-rewriter/strategies/veryfront-strategy.ts`), the same
* mechanism `veryfront/workflow` already uses.
*
* Keep the exports below in sync with {@link file://./index.ts} — everything
* except the `createHandler` / `startServer` / `toNodeHandler` value export.
*
* @module veryfront
*/

export { defineConfig, defineConfigWithEnv, mergeConfigs } from "#veryfront/config";
export type { VeryfrontConfig } from "#veryfront/config";

export { getEnv } from "#veryfront/platform";

// NOTE: the server bootstrap value export (`createHandler`, `startServer`,
// `toNodeHandler` from "#veryfront/server") is intentionally omitted here — it
// is server-only and pulls production-server.ts (top-level await) into client
// chunks. Types are erased at transform time, so re-exporting them is inert.
export type { StartServerOptions, VeryfrontHandler, VeryfrontServer } from "#veryfront/server";

export {
badRequest,
forbidden,
json,
notFound as apiNotFound,
redirect as apiRedirect,
serverError,
unauthorized,
} from "#veryfront/routing";
export type { APIContext, APIHandler, APIResponse, APIRoute } from "#veryfront/routing";

export { notFound, redirect } from "#veryfront/data";
export type {
DataContext,
InferGetServerDataProps,
PageWithData,
StaticPathsResult,
} from "#veryfront/data";

export type { MDXFrontmatter, PageContext } from "#veryfront/types";

export {
CommonSchemas,
createValidatedHandler,
createValidationError,
INPUT_VALIDATION_FAILED,
parseFormData,
parseJsonBody,
parseQueryParams,
sanitizeData,
} from "#veryfront/security";
export type { ValidatedHandlerConfig, ValidatedHandlerFunction } from "#veryfront/security";
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,27 @@ describe("VeryfrontStrategy", () => {
);
});

it("should redirect the root veryfront barrel to the client-safe barrel for browser", () => {
// Regression: the full root barrel re-exports `#veryfront/server`, which
// pulls `server/production-server.ts` (module top-level await) into client
// chunks — it 500s on the es2020 browser target and aborts hydration. A
// *used* value import (`import { getEnv } from "veryfront"`) is not
// dead-stripped, so the barrel must resolve to the server-free mirror.
const result = strategy.rewrite(
makeInfo("veryfront"),
makeCtx({ target: "browser" }),
);
assertEquals(result.specifier, "/_vf_modules/_veryfront/index.client.js");
});

it("should redirect the root veryfront barrel to the client-safe barrel for SSR", () => {
const result = strategy.rewrite(
makeInfo("veryfront"),
makeCtx({ target: "ssr" }),
);
assertEquals(result.specifier, "/_vf_modules/_veryfront/index.client.js?ssr=true");
});

it("should not apply SSR override to non-overridden modules", () => {
const result = strategy.rewrite(
makeInfo("veryfront/head"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ import {
*/
const REACT_ONLY_MODULE_OVERRIDES: Record<string, string> = {
"veryfront/workflow": "/_vf_modules/_veryfront/workflow/react/index.js",
// The root barrel re-exports the server bootstrap surface from
// `#veryfront/server`, which transitively pulls `server/production-server.ts`
// (module top-level await → cannot transform to the es2020 browser target →
// HTTP 500, aborting hydration). A *used* value import from the barrel (e.g.
// `import { getEnv } from "veryfront"`) survives dead-code stripping and drags
// the whole server graph into the client. Redirect to a client/SSR-safe mirror
// barrel that omits only the server bootstrap value export. See
// `src/index.client.ts`.
"veryfront": "/_vf_modules/_veryfront/index.client.js",
Comment thread
kojiwakayama marked this conversation as resolved.
};

export class VeryfrontStrategy implements ImportRewriteStrategy {
Expand Down
Loading