From b470f6ee1c13763e75b4c59253ed5fcfc0c3c6c6 Mon Sep 17 00:00:00 2001 From: logelog <194732487+logelog@users.noreply.github.com> Date: Thu, 16 Jul 2026 05:14:39 +0200 Subject: [PATCH] fix(core): avoid optional dialect export warnings --- .changeset/quiet-dialects-build.md | 6 ++++++ packages/cloudflare/src/index.ts | 2 ++ packages/cloudflare/tests/do-config.test.ts | 18 +++++++++++++++++- .../src/astro/integration/virtual-modules.ts | 19 ++++++++++++------- .../core/src/astro/integration/vite-config.ts | 1 + packages/core/src/db/adapters.ts | 9 +++++++++ .../astro/integration/virtual-modules.test.ts | 14 +++++++++++++- 7 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 .changeset/quiet-dialects-build.md diff --git a/.changeset/quiet-dialects-build.md b/.changeset/quiet-dialects-build.md new file mode 100644 index 0000000000..380b4e1842 --- /dev/null +++ b/.changeset/quiet-dialects-build.md @@ -0,0 +1,6 @@ +--- +"emdash": patch +"@emdash-cms/cloudflare": patch +--- + +Fixes production build warnings when database adapters do not provide the optional cold-start query coalescing dialect. diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts index 7821514038..92354de349 100644 --- a/packages/cloudflare/src/index.ts +++ b/packages/cloudflare/src/index.ts @@ -262,6 +262,7 @@ export function d1(config: D1Config): DatabaseDescriptor { config, type: "sqlite", supportsRequestScope: true, + supportsCoalescing: true, }; } @@ -370,6 +371,7 @@ export function durableObjects(config: DurableObjectsConfig): DatabaseDescriptor config, type: "sqlite", supportsRequestScope: true, + supportsCoalescing: true, }; } diff --git a/packages/cloudflare/tests/do-config.test.ts b/packages/cloudflare/tests/do-config.test.ts index 278a2fee55..51ed9c004f 100644 --- a/packages/cloudflare/tests/do-config.test.ts +++ b/packages/cloudflare/tests/do-config.test.ts @@ -1,6 +1,22 @@ import { describe, it, expect } from "vitest"; -import { previewDatabase, playgroundDatabase } from "../src/index.js"; +import { d1, durableObjects, previewDatabase, playgroundDatabase } from "../src/index.js"; + +describe("d1()", () => { + it("opts into request-scoped and coalescing dialects", () => { + const result = d1({ binding: "DB" }); + expect(result.supportsRequestScope).toBe(true); + expect(result.supportsCoalescing).toBe(true); + }); +}); + +describe("durableObjects()", () => { + it("opts into request-scoped and coalescing dialects", () => { + const result = durableObjects({ binding: "DB_DO" }); + expect(result.supportsRequestScope).toBe(true); + expect(result.supportsCoalescing).toBe(true); + }); +}); describe("previewDatabase()", () => { it("returns a sqlite DatabaseDescriptor with the DO entrypoint", () => { diff --git a/packages/core/src/astro/integration/virtual-modules.ts b/packages/core/src/astro/integration/virtual-modules.ts index 755c928629..6d5ef36c72 100644 --- a/packages/core/src/astro/integration/virtual-modules.ts +++ b/packages/core/src/astro/integration/virtual-modules.ts @@ -84,13 +84,18 @@ export function generateConfigModule(serializableConfig: Record * the generator re-exports it so middleware can ask for a per-request Kysely * (used for D1 Sessions API, bookmark cookies, read-replica routing). Other * adapters get a stub that returns null. + * + * Adapters independently opt into the cold-start coalescing dialect. Keeping + * this capability explicit prevents bundlers from probing exports that do not + * exist on SQLite, libSQL, PostgreSQL, or other adapters. */ export function generateDialectModule(opts: { entrypoint?: string; type?: string; supportsRequestScope: boolean; + supportsCoalescing: boolean; }): string { - const { entrypoint, supportsRequestScope } = opts; + const { entrypoint, supportsRequestScope, supportsCoalescing } = opts; if (!entrypoint) { return [ `export const createDialect = undefined;`, @@ -101,16 +106,16 @@ export function generateDialectModule(opts: { } const type = opts.type ?? "sqlite"; - // Namespace access (not a named re-export) so backends that don't export - // createCoalescingDialect yield `undefined` rather than a build error. - const coalescingReExport = `import * as _dialectModule from "${entrypoint}"; -export const createCoalescingDialect = _dialectModule.createCoalescingDialect;`; + const coalescingExport = supportsCoalescing + ? `import { createCoalescingDialect as _createCoalescingDialect } from "${entrypoint}"; +export const createCoalescingDialect = _createCoalescingDialect;` + : `export const createCoalescingDialect = undefined;`; if (supportsRequestScope) { return ` import { createDialect as _createDialect } from "${entrypoint}"; export { createRequestScopedDb } from "${entrypoint}"; -${coalescingReExport} +${coalescingExport} export const createDialect = _createDialect; export const dialectType = ${JSON.stringify(type)}; `; @@ -118,7 +123,7 @@ export const dialectType = ${JSON.stringify(type)}; return ` import { createDialect as _createDialect } from "${entrypoint}"; -${coalescingReExport} +${coalescingExport} export const createDialect = _createDialect; export const dialectType = ${JSON.stringify(type)}; export const createRequestScopedDb = (_opts) => null; diff --git a/packages/core/src/astro/integration/vite-config.ts b/packages/core/src/astro/integration/vite-config.ts index 102f8770a5..79c035decf 100644 --- a/packages/core/src/astro/integration/vite-config.ts +++ b/packages/core/src/astro/integration/vite-config.ts @@ -239,6 +239,7 @@ export function createVirtualModulesPlugin( entrypoint: resolvedConfig.database?.entrypoint, type: resolvedConfig.database?.type, supportsRequestScope: resolvedConfig.database?.supportsRequestScope ?? false, + supportsCoalescing: resolvedConfig.database?.supportsCoalescing ?? false, }); } // Generate a module that statically imports the configured storage diff --git a/packages/core/src/db/adapters.ts b/packages/core/src/db/adapters.ts index bf2d7859be..a4ea100ab8 100644 --- a/packages/core/src/db/adapters.ts +++ b/packages/core/src/db/adapters.ts @@ -48,6 +48,15 @@ export interface DatabaseDescriptor { * the middleware takes its default (singleton) path. */ supportsRequestScope?: boolean; + /** + * When true, the adapter's runtime entrypoint MUST export a named + * `createCoalescingDialect` function. The runtime uses this fresh dialect + * only for its cold-start read batch. + * + * When false or absent, the virtual module exports `undefined` without + * inspecting an optional entrypoint export. + */ + supportsCoalescing?: boolean; } export interface SqliteConfig { diff --git a/packages/core/tests/unit/astro/integration/virtual-modules.test.ts b/packages/core/tests/unit/astro/integration/virtual-modules.test.ts index e6a35aad48..c9afbfffa6 100644 --- a/packages/core/tests/unit/astro/integration/virtual-modules.test.ts +++ b/packages/core/tests/unit/astro/integration/virtual-modules.test.ts @@ -37,7 +37,10 @@ describe("generateConfigModule", () => { describe("generateDialectModule", () => { it("emits undefined createDialect and null stub when no entrypoint is configured", () => { - const out = generateDialectModule({ supportsRequestScope: false }); + const out = generateDialectModule({ + supportsRequestScope: false, + supportsCoalescing: false, + }); expect(out).toContain("export const createDialect = undefined"); expect(out).toContain("export const createRequestScopedDb = (_opts) => null"); }); @@ -47,9 +50,12 @@ describe("generateDialectModule", () => { entrypoint: "some-adapter/dialect", type: "sqlite", supportsRequestScope: false, + supportsCoalescing: false, }); expect(out).toContain(`import { createDialect as _createDialect } from "some-adapter/dialect"`); expect(out).toContain("export const createRequestScopedDb = (_opts) => null"); + expect(out).toContain("export const createCoalescingDialect = undefined"); + expect(out).not.toContain("_dialectModule.createCoalescingDialect"); expect(out).not.toContain(`export { createRequestScopedDb } from`); }); @@ -58,8 +64,13 @@ describe("generateDialectModule", () => { entrypoint: "@emdash-cms/cloudflare/db/d1", type: "sqlite", supportsRequestScope: true, + supportsCoalescing: true, }); expect(out).toContain(`export { createRequestScopedDb } from "@emdash-cms/cloudflare/db/d1"`); + expect(out).toContain( + `import { createCoalescingDialect as _createCoalescingDialect } from "@emdash-cms/cloudflare/db/d1"`, + ); + expect(out).toContain("export const createCoalescingDialect = _createCoalescingDialect"); expect(out).not.toContain("= () => null"); expect(out).not.toContain("= (_opts) => null"); }); @@ -69,6 +80,7 @@ describe("generateDialectModule", () => { entrypoint: "emdash/db/postgres", type: "postgres", supportsRequestScope: false, + supportsCoalescing: false, }); expect(out).toContain(`export const dialectType = "postgres"`); });