import { describe, it, expect, vi } from "vitest";
import { exposeInternalIds, } from "../expose-internal-ids.ts";
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function createPlugin(opts) {
    const plugin = exposeInternalIds(opts);
    return plugin;
}
const ROOT = "/project";
const FILE_ID = "/project/src/urls.tsx";
function initDev(plugin) {
    plugin.configResolved({ command: "serve", root: ROOT });
}
function rscCtx() {
    return { environment: { name: "rsc" }, warn: vi.fn() };
}
function clientCtx() {
    return { environment: { name: "client" }, warn: vi.fn() };
}
/**
 * Extract `import { __sh_xxx } from "virtual:handler-extract:..."` entries
 * from transformed code.
 */
function extractVirtualImports(code) {
    const results = [];
    const re = /import\s*\{\s*(__sh_\w+)\s*\}\s*from\s*"(virtual:handler-extract:[^"]+)"/g;
    let m;
    while ((m = re.exec(code)) !== null) {
        results.push({ exportName: m[1], specifier: m[2] });
    }
    return results;
}
// ---------------------------------------------------------------------------
// Test sources
// ---------------------------------------------------------------------------
const INLINE_SOURCE = `import { Static } from "@rangojs/router";
layout(Static(() => <nav />));
`;
const EXPORT_SOURCE = `import { Static } from "@rangojs/router";
export const Nav = Static(() => <nav />);
`;
const MIXED_SOURCE = `import { Static } from "@rangojs/router";
export const Nav = Static(() => <nav />);
layout(Static(() => <sidebar />));
`;
const SAME_LINE_SOURCE = `import { Static } from "@rangojs/router";
layout(Static(() => <a />), Static(() => <b />));
`;
const PRERENDER_INLINE_SOURCE = `import { Prerender } from "@rangojs/router";
path("/about", Prerender(() => <div>About</div>));
`;
const PRERENDER_EXPORT_SOURCE = `import { Prerender } from "@rangojs/router";
export const AboutPage = Prerender(() => <div>About</div>);
`;
const PRERENDER_MIXED_SOURCE = `import { Prerender } from "@rangojs/router";
export const AboutPage = Prerender(() => <main />);
path("/inline", Prerender(() => <aside />));
`;
const PRERENDER_INLINE_WHITESPACE_SOURCE = `import { Prerender } from "@rangojs/router";
path("/about", Prerender
(() => <div>About</div>));
`;
const STATIC_ALIAS_INLINE_SOURCE = `import { Static as sh } from "@rangojs/router";
layout(sh(() => <nav />));
`;
const STATIC_ALIAS_EXPORT_SOURCE = `import { Static as sh } from "@rangojs/router";
export const Nav = sh(() => <nav />);
`;
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
describe("exposeInternalIds - inline static handler integration", () => {
    // -----------------------------------------------------------------------
    // Full round-trip: RSC dev mode
    // -----------------------------------------------------------------------
    describe("full round-trip: RSC dev mode", () => {
        it("transforms inline call -> resolves virtual -> loads -> injects $$id", () => {
            const plugin = createPlugin();
            initDev(plugin);
            // Step 1: Transform source with inline call in RSC env
            const r1 = plugin.transform.call(rscCtx(), INLINE_SOURCE, FILE_ID);
            expect(r1).toBeDefined();
            // Should have a virtual module import
            const vImports = extractVirtualImports(r1.code);
            expect(vImports).toHaveLength(1);
            const { exportName, specifier } = vImports[0];
            expect(exportName).toMatch(/^__sh_[0-9a-f]{8}$/);
            // The inline call should be replaced with the import name
            expect(r1.code).toContain(`layout(${exportName})`);
            expect(r1.code).not.toContain("Static(() => <nav />)");
            // Step 2: resolveId adds \0 prefix
            const resolved = plugin.resolveId.call({}, specifier, FILE_ID);
            expect(resolved).toBe("\0" + specifier);
            // Step 3: load synthesises the virtual module
            const loaded = plugin.load.call({}, resolved);
            expect(loaded).toBeDefined();
            expect(loaded).toContain(`export const ${exportName}`);
            expect(loaded).toContain("Static");
            expect(loaded).toContain("@rangojs/router");
            // Step 4: transform virtual module in RSC injects $$id
            const r2 = plugin.transform.call(rscCtx(), loaded, resolved);
            expect(r2).toBeDefined();
            expect(r2.code).toContain("$$id");
            expect(r2.code).toContain(`${exportName}.$$id`);
        });
    });
    // -----------------------------------------------------------------------
    // Full round-trip: non-RSC (client/SSR)
    // -----------------------------------------------------------------------
    describe("full round-trip: non-RSC (client/SSR)", () => {
        it("replaces virtual module with stub in client env", () => {
            const plugin = createPlugin();
            initDev(plugin);
            // Populate virtual registry via RSC transform
            const r1 = plugin.transform.call(rscCtx(), INLINE_SOURCE, FILE_ID);
            const { exportName, specifier } = extractVirtualImports(r1.code)[0];
            const resolved = plugin.resolveId.call({}, specifier, FILE_ID);
            const loaded = plugin.load.call({}, resolved);
            // Transform virtual module in client env -> stub
            const r2 = plugin.transform.call(clientCtx(), loaded, resolved);
            expect(r2).toBeDefined();
            expect(r2.code).toContain("__brand");
            expect(r2.code).toContain('"staticHandler"');
            expect(r2.code).toContain("$$id");
            // No Static call remains
            expect(r2.code).not.toContain("Static(");
        });
    });
    // -----------------------------------------------------------------------
    // Mixed files (export const + inline)
    // -----------------------------------------------------------------------
    describe("mixed files (export const + inline)", () => {
        it("extracts inline, leaves export const, both get $$id in RSC", () => {
            const plugin = createPlugin();
            initDev(plugin);
            // Transform mixed source in RSC
            const r1 = plugin.transform.call(rscCtx(), MIXED_SOURCE, FILE_ID);
            expect(r1).toBeDefined();
            // Inline call extracted to virtual module
            const vImports = extractVirtualImports(r1.code);
            expect(vImports).toHaveLength(1);
            // Export const Nav stays in the original file with $$id injected
            expect(r1.code).toContain("export const Nav = Static");
            expect(r1.code).toContain("Nav.$$id");
            // Inline call replaced with import name
            expect(r1.code).toContain(`layout(${vImports[0].exportName})`);
            expect(r1.code).not.toContain("Static(() => <sidebar />)");
            // Virtual module also gets $$id in RSC
            const resolved = plugin.resolveId.call({}, vImports[0].specifier, FILE_ID);
            const loaded = plugin.load.call({}, resolved);
            const r2 = plugin.transform.call(rscCtx(), loaded, resolved);
            expect(r2).toBeDefined();
            expect(r2.code).toContain(`${vImports[0].exportName}.$$id`);
        });
    });
    // -----------------------------------------------------------------------
    // Module tracking (RSC build mode)
    // -----------------------------------------------------------------------
    describe("module tracking (RSC build mode)", () => {
        it("populates staticHandlerModules for export const patterns", () => {
            const plugin = createPlugin({ forceBuild: true });
            initDev(plugin); // forceBuild overrides command
            plugin.transform.call(rscCtx(), EXPORT_SOURCE, FILE_ID);
            expect(plugin.api.staticHandlerModules.get(FILE_ID)).toEqual(["Nav"]);
        });
        it("tracks virtual module exports in build mode", () => {
            const plugin = createPlugin({ forceBuild: true });
            initDev(plugin);
            // Populate registry via RSC transform
            const r1 = plugin.transform.call(rscCtx(), INLINE_SOURCE, FILE_ID);
            const { exportName, specifier } = extractVirtualImports(r1.code)[0];
            const resolved = plugin.resolveId.call({}, specifier, FILE_ID);
            const loaded = plugin.load.call({}, resolved);
            // Transform virtual module in RSC build mode
            plugin.transform.call(rscCtx(), loaded, resolved);
            expect(plugin.api.staticHandlerModules.get(resolved)).toEqual([
                exportName,
            ]);
        });
    });
    // -----------------------------------------------------------------------
    // Virtual module import resolution
    // -----------------------------------------------------------------------
    describe("virtual module import resolution", () => {
        it("resolves imports from virtual modules against original file", () => {
            const plugin = createPlugin();
            initDev(plugin);
            // Populate registry
            const r1 = plugin.transform.call(rscCtx(), INLINE_SOURCE, FILE_ID);
            const { specifier } = extractVirtualImports(r1.code)[0];
            const resolved = plugin.resolveId.call({}, specifier, FILE_ID);
            // Resolve an import from within the virtual module
            const ctx = {
                resolve: vi
                    .fn()
                    .mockResolvedValue({ id: "/project/src/components/Nav.tsx" }),
            };
            plugin.resolveId.call(ctx, "@rangojs/router", resolved);
            expect(ctx.resolve).toHaveBeenCalledWith("@rangojs/router", FILE_ID, { skipSelf: true });
        });
    });
    // -----------------------------------------------------------------------
    // Server-only import eviction (node:fs in Cloudflare)
    // -----------------------------------------------------------------------
    describe("server-only import eviction", () => {
        it("virtual module stub drops node:fs import (inline call path)", () => {
            const plugin = createPlugin();
            initDev(plugin);
            const source = `import { Static } from "@rangojs/router";
import { readFileSync } from "node:fs";
layout(Static(() => {
  const data = readFileSync("data.json", "utf-8");
  return <pre>{data}</pre>;
}));
`;
            // RSC transform populates virtual registry
            const r1 = plugin.transform.call(rscCtx(), source, FILE_ID);
            const { exportName, specifier } = extractVirtualImports(r1.code)[0];
            const resolved = plugin.resolveId.call({}, specifier, FILE_ID);
            const loaded = plugin.load.call({}, resolved);
            // Virtual module should contain node:fs (needed for RSC build)
            expect(loaded).toContain("node:fs");
            expect(loaded).toContain("readFileSync");
            // Client transform should produce stub WITHOUT node:fs
            const r2 = plugin.transform.call(clientCtx(), loaded, resolved);
            expect(r2).toBeDefined();
            expect(r2.code).not.toContain("node:fs");
            expect(r2.code).not.toContain("readFileSync");
            expect(r2.code).toContain("__brand");
            expect(r2.code).toContain('"staticHandler"');
        });
        it("whole-file stub drops node:fs import (export const path)", () => {
            const plugin = createPlugin();
            initDev(plugin);
            const source = `import { Static } from "@rangojs/router";
import { readFileSync } from "node:fs";
export const Nav = Static(() => {
  const data = readFileSync("data.json", "utf-8");
  return <pre>{data}</pre>;
});
`;
            // Client transform should produce stub WITHOUT node:fs
            const r = plugin.transform.call(clientCtx(), source, FILE_ID);
            expect(r).toBeDefined();
            expect(r.code).not.toContain("node:fs");
            expect(r.code).not.toContain("readFileSync");
            expect(r.code).toContain("__brand");
            expect(r.code).toContain('"staticHandler"');
        });
    });
    // -----------------------------------------------------------------------
    // Edge cases
    // -----------------------------------------------------------------------
    describe("edge cases", () => {
        it("no relevant imports -> transform returns undefined", () => {
            const plugin = createPlugin();
            initDev(plugin);
            const code = `import { route } from "@rangojs/router";\nroute("/", handler);`;
            const result = plugin.transform.call(rscCtx(), code, FILE_ID);
            expect(result).toBeUndefined();
        });
        it("only export const -> no virtual modules, $$id injected in RSC", () => {
            const plugin = createPlugin();
            initDev(plugin);
            const result = plugin.transform.call(rscCtx(), EXPORT_SOURCE, FILE_ID);
            expect(result).toBeDefined();
            // No virtual module imports
            expect(extractVirtualImports(result.code)).toHaveLength(0);
            // $$id injected
            expect(result.code).toContain("Nav.$$id");
        });
        it("same-line collisions -> distinct virtual modules", () => {
            const plugin = createPlugin();
            initDev(plugin);
            const result = plugin.transform.call(rscCtx(), SAME_LINE_SOURCE, FILE_ID);
            expect(result).toBeDefined();
            const vImports = extractVirtualImports(result.code);
            expect(vImports).toHaveLength(2);
            // Different export names
            expect(vImports[0].exportName).not.toBe(vImports[1].exportName);
            // Different specifiers
            expect(vImports[0].specifier).not.toBe(vImports[1].specifier);
        });
        it("extracts inline call when fn name and paren are split by newline", () => {
            const plugin = createPlugin();
            initDev(plugin);
            const result = plugin.transform.call(rscCtx(), PRERENDER_INLINE_WHITESPACE_SOURCE, FILE_ID);
            expect(result).toBeDefined();
            const vImports = extractVirtualImports(result.code);
            expect(vImports).toHaveLength(1);
            expect(result.code).toContain(`path("/about", ${vImports[0].exportName})`);
            expect(result.code).not.toContain("Prerender\n(");
        });
        it("load returns null for unregistered virtual ID", () => {
            const plugin = createPlugin();
            initDev(plugin);
            const result = plugin.load.call({}, "\0virtual:handler-extract:nonexistent:99");
            expect(result).toBeNull();
        });
        it("extracts inline call when Static is imported with alias", () => {
            const plugin = createPlugin();
            initDev(plugin);
            const result = plugin.transform.call(rscCtx(), STATIC_ALIAS_INLINE_SOURCE, FILE_ID);
            expect(result).toBeDefined();
            const vImports = extractVirtualImports(result.code);
            expect(vImports).toHaveLength(1);
            expect(result.code).toContain(`layout(${vImports[0].exportName})`);
        });
        it("injects $$id for export const alias call", () => {
            const plugin = createPlugin();
            initDev(plugin);
            const result = plugin.transform.call(rscCtx(), STATIC_ALIAS_EXPORT_SOURCE, FILE_ID);
            expect(result).toBeDefined();
            expect(result.code).toContain("Nav.$$id");
        });
    });
});
// ---------------------------------------------------------------------------
// Inline Prerender integration
// ---------------------------------------------------------------------------
describe("exposeInternalIds - inline prerender handler integration", () => {
    describe("full round-trip: RSC dev mode", () => {
        it("transforms inline Prerender -> virtual -> $$id", () => {
            const plugin = createPlugin();
            initDev(plugin);
            // Step 1: Transform source with inline call in RSC env
            const r1 = plugin.transform.call(rscCtx(), PRERENDER_INLINE_SOURCE, FILE_ID);
            expect(r1).toBeDefined();
            // Should have a virtual module import
            const vImports = extractVirtualImports(r1.code);
            expect(vImports).toHaveLength(1);
            const { exportName, specifier } = vImports[0];
            expect(exportName).toMatch(/^__sh_[0-9a-f]{8}$/);
            // The inline call should be replaced with the import name
            expect(r1.code).toContain(`path("/about", ${exportName})`);
            expect(r1.code).not.toContain("Prerender(() =>");
            // Step 2: resolveId adds \0 prefix
            const resolved = plugin.resolveId.call({}, specifier, FILE_ID);
            expect(resolved).toBe("\0" + specifier);
            // Step 3: load synthesises the virtual module
            const loaded = plugin.load.call({}, resolved);
            expect(loaded).toBeDefined();
            expect(loaded).toContain(`export const ${exportName}`);
            expect(loaded).toContain("Prerender");
            expect(loaded).toContain("@rangojs/router");
            // Step 4: transform virtual module in RSC injects $$id
            const r2 = plugin.transform.call(rscCtx(), loaded, resolved);
            expect(r2).toBeDefined();
            expect(r2.code).toContain("$$id");
            expect(r2.code).toContain(`${exportName}.$$id`);
        });
    });
    describe("full round-trip: non-RSC (client/SSR)", () => {
        it("replaces virtual prerender module with stub in client env", () => {
            const plugin = createPlugin();
            initDev(plugin);
            // Populate virtual registry via RSC transform
            const r1 = plugin.transform.call(rscCtx(), PRERENDER_INLINE_SOURCE, FILE_ID);
            const { exportName, specifier } = extractVirtualImports(r1.code)[0];
            const resolved = plugin.resolveId.call({}, specifier, FILE_ID);
            const loaded = plugin.load.call({}, resolved);
            // Transform virtual module in client env -> stub
            const r2 = plugin.transform.call(clientCtx(), loaded, resolved);
            expect(r2).toBeDefined();
            expect(r2.code).toContain("__brand");
            expect(r2.code).toContain('"prerenderHandler"');
            expect(r2.code).toContain("$$id");
            // No Prerender call remains
            expect(r2.code).not.toContain("Prerender(");
        });
    });
    describe("mixed files (export const + inline)", () => {
        it("extracts inline, leaves export const, both get $$id in RSC", () => {
            const plugin = createPlugin();
            initDev(plugin);
            // Transform mixed source in RSC
            const r1 = plugin.transform.call(rscCtx(), PRERENDER_MIXED_SOURCE, FILE_ID);
            expect(r1).toBeDefined();
            // Inline call extracted to virtual module
            const vImports = extractVirtualImports(r1.code);
            expect(vImports).toHaveLength(1);
            // Export const stays in the original file with $$id injected
            expect(r1.code).toContain("export const AboutPage = Prerender");
            expect(r1.code).toContain("AboutPage.$$id");
            // Inline call replaced with import name
            expect(r1.code).toContain(`path("/inline", ${vImports[0].exportName})`);
            expect(r1.code).not.toContain("Prerender(() => <aside />)");
        });
    });
    describe("module tracking (RSC build mode)", () => {
        it("populates prerenderHandlerModules for export const patterns", () => {
            const plugin = createPlugin({ forceBuild: true });
            initDev(plugin);
            plugin.transform.call(rscCtx(), PRERENDER_EXPORT_SOURCE, FILE_ID);
            expect(plugin.api.prerenderHandlerModules.get(FILE_ID)).toEqual(["AboutPage"]);
        });
    });
});
describe("exposeInternalIds - unsupported shape diagnostics", () => {
    it("warns for createLoader when declaration uses let", () => {
        const plugin = createPlugin();
        initDev(plugin);
        const code = `import { createLoader } from "@rangojs/router";
export let LocalLoader = createLoader(async () => ({ ok: true }));
`;
        const ctx = rscCtx();
        plugin.transform.call(ctx, code, FILE_ID);
        expect(ctx.warn).toHaveBeenCalledTimes(1);
        const [msg] = ctx.warn.mock.calls[0];
        expect(msg).toContain("Unsupported createLoader shape");
        expect(msg).toContain("Supported shapes are:");
    });
    it("does not warn for supported createLoader export const shape", () => {
        const plugin = createPlugin();
        initDev(plugin);
        const code = `import { createLoader } from "@rangojs/router";
export const MyLoader = createLoader(async () => ({ ok: true }));
`;
        const ctx = rscCtx();
        plugin.transform.call(ctx, code, FILE_ID);
        expect(ctx.warn).not.toHaveBeenCalled();
    });
    it("warns at most once per file/function shape", () => {
        const plugin = createPlugin();
        initDev(plugin);
        const code = `import { createHandle } from "@rangojs/router";
layout(createHandle(() => []));
`;
        const ctx = rscCtx();
        plugin.transform.call(ctx, code, FILE_ID);
        plugin.transform.call(ctx, code, FILE_ID);
        expect(ctx.warn).toHaveBeenCalledTimes(1);
        const [msg] = ctx.warn.mock.calls[0];
        expect(msg).toContain("Unsupported createHandle shape");
    });
});
describe("exposeInternalIds - alias support (strict create APIs)", () => {
    it("injects $$id for createLoader imported with alias", () => {
        const plugin = createPlugin();
        initDev(plugin);
        const code = `import { createLoader as cl } from "@rangojs/router";
export const MyLoader = cl(async () => ({ ok: true }));
`;
        const result = plugin.transform.call(rscCtx(), code, FILE_ID);
        expect(result).toBeDefined();
        expect(result.code).toContain("MyLoader.$$id");
    });
    it("injects $$id for createHandle imported with alias", () => {
        const plugin = createPlugin();
        initDev(plugin);
        const code = `import { createHandle as ch } from "@rangojs/router";
export const Breadcrumbs = ch(() => []);
`;
        const result = plugin.transform.call(rscCtx(), code, FILE_ID);
        expect(result).toBeDefined();
        expect(result.code).toContain("Breadcrumbs.$$id");
    });
    it("injects __rsc_ls_key for createLocationState imported with alias", () => {
        const plugin = createPlugin();
        initDev(plugin);
        const code = `import { createLocationState as cls } from "@rangojs/router";
export const ProductState = cls<string>();
`;
        const result = plugin.transform.call(rscCtx(), code, FILE_ID);
        expect(result).toBeDefined();
        expect(result.code).toContain("ProductState.__rsc_ls_key");
    });
    it("supports createLoader declared as const and exported via specifier alias", () => {
        const plugin = createPlugin();
        initDev(plugin);
        const code = `import { createLoader } from "@rangojs/router";
const LocalLoader = createLoader(async () => ({ ok: true }));
export { LocalLoader as PublicLoader };
`;
        const ctx = rscCtx();
        const result = plugin.transform.call(ctx, code, FILE_ID);
        expect(result).toBeDefined();
        expect(result.code).toContain('LocalLoader.$$id =');
        expect(ctx.warn).not.toHaveBeenCalled();
    });
    it("supports createHandle declared as const and exported via specifier", () => {
        const plugin = createPlugin();
        initDev(plugin);
        const code = `import { createHandle } from "@rangojs/router";
const LocalHandle = createHandle(() => []);
export { LocalHandle };
`;
        const result = plugin.transform.call(rscCtx(), code, FILE_ID);
        expect(result).toBeDefined();
        expect(result.code).toContain("LocalHandle.$$id");
    });
    it("supports createLocationState declared as const and exported via specifier alias", () => {
        const plugin = createPlugin();
        initDev(plugin);
        const code = `import { createLocationState } from "@rangojs/router";
const ProductStateDef = createLocationState<string>();
export { ProductStateDef as ProductState };
`;
        const result = plugin.transform.call(rscCtx(), code, FILE_ID);
        expect(result).toBeDefined();
        expect(result.code).toContain("ProductStateDef.__rsc_ls_key");
    });
});
describe("exposeInternalIds - handler export specifiers", () => {
    it("tracks static handler exported via specifier alias in build mode", () => {
        const plugin = createPlugin({ forceBuild: true });
        initDev(plugin);
        const code = `import { Static } from "@rangojs/router";
const DocsNav = Static(() => <nav />);
export { DocsNav as DocsNavPublic };
`;
        plugin.transform.call(rscCtx(), code, FILE_ID);
        expect(plugin.api.staticHandlerModules.get(FILE_ID)).toEqual(["DocsNavPublic"]);
    });
    it("tracks prerender handler exported via specifier alias in build mode", () => {
        const plugin = createPlugin({ forceBuild: true });
        initDev(plugin);
        const code = `import { Prerender } from "@rangojs/router";
const DocsPage = Prerender(() => <div />);
export { DocsPage as DocsPagePublic };
`;
        plugin.transform.call(rscCtx(), code, FILE_ID);
        expect(plugin.api.prerenderHandlerModules.get(FILE_ID)).toEqual(["DocsPagePublic"]);
    });
});
// ---------------------------------------------------------------------------
// Client loader stubs for const + export { X } pattern
// ---------------------------------------------------------------------------
describe("exposeInternalIds - client loader stubs for const + export patterns", () => {
    it("generates client stub for const + export { X }", () => {
        const plugin = createPlugin();
        initDev(plugin);
        const code = `import { createLoader } from "@rangojs/router";
const MyLoader = createLoader(async () => ({ ok: true }));
export { MyLoader };
`;
        const result = plugin.transform.call(clientCtx(), code, FILE_ID);
        expect(result).toBeDefined();
        expect(result.code).toContain('__brand: "loader"');
        expect(result.code).toContain("$$id");
        expect(result.code).toContain("export const MyLoader");
        // Entire file replaced - no server code
        expect(result.code).not.toContain("async");
    });
    it("generates client stub for const + export { X as Y }", () => {
        const plugin = createPlugin();
        initDev(plugin);
        const code = `import { createLoader } from "@rangojs/router";
const InternalLoader = createLoader(async () => ({ ok: true }));
export { InternalLoader as PublicLoader };
`;
        const result = plugin.transform.call(clientCtx(), code, FILE_ID);
        expect(result).toBeDefined();
        expect(result.code).toContain('__brand: "loader"');
        expect(result.code).toContain("export const PublicLoader");
        // Uses the exported name, not the local name
        expect(result.code).not.toContain("InternalLoader");
        expect(result.code).not.toContain("async");
    });
    it("does not generate loader-only stub when file has mixed exports (const + export pattern)", () => {
        const plugin = createPlugin();
        initDev(plugin);
        const code = `import { createLoader } from "@rangojs/router";
const MyLoader = createLoader(async () => ({ ok: true }));
export { MyLoader };
export const helperFn = () => "not a loader";
`;
        const result = plugin.transform.call(clientCtx(), code, FILE_ID);
        // Should NOT replace entire file since helperFn is not a loader
        if (result) {
            expect(result.code).toContain("helperFn");
        }
    });
});
// ---------------------------------------------------------------------------
// Whole-file handler stubs for const + export { X } pattern
// ---------------------------------------------------------------------------
describe("exposeInternalIds - whole-file handler stubs for const + export patterns", () => {
    it("generates whole-file stub for static handler with const + export { X }", () => {
        const plugin = createPlugin();
        initDev(plugin);
        const code = `import { Static } from "@rangojs/router";
const Nav = Static(() => <nav />);
export { Nav };
`;
        const result = plugin.transform.call(clientCtx(), code, FILE_ID);
        expect(result).toBeDefined();
        expect(result.code).toContain('__brand: "staticHandler"');
        expect(result.code).toContain("$$id");
        expect(result.code).toContain("export const Nav");
        expect(result.code).not.toContain("Static");
    });
    it("generates whole-file stub for prerender handler with const + export { X as Y }", () => {
        const plugin = createPlugin();
        initDev(plugin);
        const code = `import { Prerender } from "@rangojs/router";
const InternalPage = Prerender(() => <div />);
export { InternalPage as PublicPage };
`;
        const result = plugin.transform.call(clientCtx(), code, FILE_ID);
        expect(result).toBeDefined();
        expect(result.code).toContain('__brand: "prerenderHandler"');
        expect(result.code).toContain("export const PublicPage");
        expect(result.code).not.toContain("InternalPage");
        expect(result.code).not.toContain("Prerender");
    });
});
// ---------------------------------------------------------------------------
// Expression stubs for const + export { X } pattern
// ---------------------------------------------------------------------------
describe("exposeInternalIds - expr stubs for const + export patterns", () => {
    it("overwrites call expression for static handler with const + export { X }", () => {
        const plugin = createPlugin();
        initDev(plugin);
        // Mixed file: has a non-handler export, so whole-file stub won't apply
        const code = `import { Static } from "@rangojs/router";
const Nav = Static(() => <nav />);
export { Nav };
export const PAGE_TITLE = "docs";
`;
        const result = plugin.transform.call(clientCtx(), code, FILE_ID);
        expect(result).toBeDefined();
        expect(result.code).toContain('__brand: "staticHandler"');
        expect(result.code).toContain("$$id");
        // The non-handler export is preserved
        expect(result.code).toContain("PAGE_TITLE");
        // The call expression is replaced with a stub object
        expect(result.code).not.toContain("Static(");
    });
    it("overwrites call expression for handler with const + export { X as Y }", () => {
        const plugin = createPlugin();
        initDev(plugin);
        const code = `import { Static } from "@rangojs/router";
const Nav = Static(() => <nav />);
export { Nav as PublicNav };
export const PAGE_TITLE = "docs";
`;
        const result = plugin.transform.call(clientCtx(), code, FILE_ID);
        expect(result).toBeDefined();
        expect(result.code).toContain('__brand: "staticHandler"');
        expect(result.code).toContain("$$id");
        expect(result.code).toContain("PAGE_TITLE");
        expect(result.code).not.toContain("Static(");
    });
});
//# sourceMappingURL=expose-internal-ids.test.js.map