fix(compiler): route the generated api object through the real buildApiRequest
Root-cause fix for the fix-round-1 review: the inlined query/body assembly in __wrnexusCallApi was a third, unguarded copy of buildApiRequest's rules. Restore the import of buildApiRequest from @wrnexus/core in the generated module and delete the inline copy. The four api-block-ssr.test.ts tests (and three in compiler.test.ts) that dynamically import a generated module from an OS tmpdir were failing against a stale globally-installed @wrnexus/core (v0.8.8, predates buildApiRequest) because that tmpdir has no node_modules of its own and bare-specifier resolution walked out of the workspace. Fixed at the source: symlink the workspace @wrnexus/core into each tmpdir root before the dynamic import, the same way every in-repo package already resolves it.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
// Generated by scripts/build-editor-compiler.mjs. Do not edit directly.
|
// Generated by scripts/build-editor-compiler.mjs. Do not edit directly.
|
||||||
// WRN editor compiler source hash: 03f56cb1555bb66c503f2ec4caf75aa149dd7142637593a07c78d1f1baab8e2b
|
// WRN editor compiler source hash: 843d73711698bc31e874370cb28bf0cb6940d3d4a6c761d00921b5f35806ab98
|
||||||
// WRN editor compiler generator hash: a54ca847c758bc98d8e353ad6d70088df31de1820f6cf9d1c3462505f563e6b8
|
// WRN editor compiler generator hash: a54ca847c758bc98d8e353ad6d70088df31de1820f6cf9d1c3462505f563e6b8
|
||||||
// Generated with TypeScript: 6.0.3
|
// Generated with TypeScript: 6.0.3
|
||||||
const __nodeRequire = require;
|
const __nodeRequire = require;
|
||||||
@@ -1691,28 +1691,15 @@ async function __wrnexusCallApi(
|
|||||||
return await ctx.__wrnexusCallApi(path, method, input);
|
return await ctx.__wrnexusCallApi(path, method, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
const verb = String(method || "GET").toUpperCase();
|
const built = __wrnexusBuildApiRequest(path, method, input as Record<string, unknown> | undefined);
|
||||||
const values = (input ?? {}) as Record<string, unknown>;
|
const url = new URL(built.url, ctx.req.url);
|
||||||
const headers = new Headers(ctx.req.headers);
|
const headers = new Headers(ctx.req.headers);
|
||||||
let requestPath = path;
|
if (built.contentType) headers.set("content-type", built.contentType);
|
||||||
let body: string | undefined;
|
|
||||||
if (verb === "GET" || verb === "HEAD") {
|
|
||||||
const query: string[] = [];
|
|
||||||
for (const [key, value] of Object.entries(values)) {
|
|
||||||
if (value === undefined || value === null || value === "") continue;
|
|
||||||
query.push(\`\${encodeURIComponent(key)}=\${encodeURIComponent(String(value))}\`);
|
|
||||||
}
|
|
||||||
if (query.length) requestPath = \`\${path}?\${query.join("&")}\`;
|
|
||||||
} else {
|
|
||||||
body = JSON.stringify(values);
|
|
||||||
headers.set("content-type", "application/json");
|
|
||||||
}
|
|
||||||
const url = new URL(requestPath, ctx.req.url);
|
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
new Request(url, {
|
new Request(url, {
|
||||||
method,
|
method,
|
||||||
headers,
|
headers,
|
||||||
...(body === undefined ? {} : { body }),
|
...(built.body === undefined ? {} : { body: built.body }),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
const type = res.headers.get("content-type") || "";
|
const type = res.headers.get("content-type") || "";
|
||||||
@@ -2203,6 +2190,7 @@ function generateInner(ast) {
|
|||||||
const needsSsrRuntime = ssrBindings.length > 0 || loops.length > 0 || runtimeStateNames.size > 0;
|
const needsSsrRuntime = ssrBindings.length > 0 || loops.length > 0 || runtimeStateNames.size > 0;
|
||||||
const needsRuntimeHelpers = needsSsrRuntime || hasServerApis;
|
const needsRuntimeHelpers = needsSsrRuntime || hasServerApis;
|
||||||
if (needsRuntimeHelpers) {
|
if (needsRuntimeHelpers) {
|
||||||
|
out.push(`import { buildApiRequest as __wrnexusBuildApiRequest } from "@wrnexus/core";`);
|
||||||
out.push(ssrRuntimeSource());
|
out.push(ssrRuntimeSource());
|
||||||
out.push(`const __wrnexusSsrBindings: __WrnexusSsrBinding[] = ${JSON.stringify(ssrBindings, null, 2)};`);
|
out.push(`const __wrnexusSsrBindings: __WrnexusSsrBinding[] = ${JSON.stringify(ssrBindings, null, 2)};`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// WRN editor extension source hash: ae7dab4aff1ce30f0d01fa0b9172651e526408916351862e2644a85c25df03ac
|
// WRN editor extension source hash: 52dfa294ba556cb26807ce112cc6826a690aa4a80721520d0c39bd7cfd8aa33e
|
||||||
// WRN editor extension generator hash: 456d1d614e44e5fb1f19b784176c09cf2ade9b64ef73a17934c2698150b62728
|
// WRN editor extension generator hash: 456d1d614e44e5fb1f19b784176c09cf2ade9b64ef73a17934c2698150b62728
|
||||||
"use strict";
|
"use strict";
|
||||||
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
||||||
|
|||||||
@@ -1068,28 +1068,15 @@ async function __wrnexusCallApi(
|
|||||||
return await ctx.__wrnexusCallApi(path, method, input);
|
return await ctx.__wrnexusCallApi(path, method, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
const verb = String(method || "GET").toUpperCase();
|
const built = __wrnexusBuildApiRequest(path, method, input as Record<string, unknown> | undefined);
|
||||||
const values = (input ?? {}) as Record<string, unknown>;
|
const url = new URL(built.url, ctx.req.url);
|
||||||
const headers = new Headers(ctx.req.headers);
|
const headers = new Headers(ctx.req.headers);
|
||||||
let requestPath = path;
|
if (built.contentType) headers.set("content-type", built.contentType);
|
||||||
let body: string | undefined;
|
|
||||||
if (verb === "GET" || verb === "HEAD") {
|
|
||||||
const query: string[] = [];
|
|
||||||
for (const [key, value] of Object.entries(values)) {
|
|
||||||
if (value === undefined || value === null || value === "") continue;
|
|
||||||
query.push(\`\${encodeURIComponent(key)}=\${encodeURIComponent(String(value))}\`);
|
|
||||||
}
|
|
||||||
if (query.length) requestPath = \`\${path}?\${query.join("&")}\`;
|
|
||||||
} else {
|
|
||||||
body = JSON.stringify(values);
|
|
||||||
headers.set("content-type", "application/json");
|
|
||||||
}
|
|
||||||
const url = new URL(requestPath, ctx.req.url);
|
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
new Request(url, {
|
new Request(url, {
|
||||||
method,
|
method,
|
||||||
headers,
|
headers,
|
||||||
...(body === undefined ? {} : { body }),
|
...(built.body === undefined ? {} : { body: built.body }),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
const type = res.headers.get("content-type") || "";
|
const type = res.headers.get("content-type") || "";
|
||||||
@@ -1663,6 +1650,7 @@ function generateInner(ast: PageAst): string {
|
|||||||
const needsSsrRuntime = ssrBindings.length > 0 || loops.length > 0 || runtimeStateNames.size > 0;
|
const needsSsrRuntime = ssrBindings.length > 0 || loops.length > 0 || runtimeStateNames.size > 0;
|
||||||
const needsRuntimeHelpers = needsSsrRuntime || hasServerApis;
|
const needsRuntimeHelpers = needsSsrRuntime || hasServerApis;
|
||||||
if (needsRuntimeHelpers) {
|
if (needsRuntimeHelpers) {
|
||||||
|
out.push(`import { buildApiRequest as __wrnexusBuildApiRequest } from "@wrnexus/core";`);
|
||||||
out.push(ssrRuntimeSource());
|
out.push(ssrRuntimeSource());
|
||||||
out.push(
|
out.push(
|
||||||
`const __wrnexusSsrBindings: __WrnexusSsrBinding[] = ${JSON.stringify(ssrBindings, null, 2)};`,
|
`const __wrnexusSsrBindings: __WrnexusSsrBinding[] = ${JSON.stringify(ssrBindings, null, 2)};`,
|
||||||
|
|||||||
@@ -1,10 +1,29 @@
|
|||||||
import { afterEach, expect, test } from "bun:test";
|
import { afterEach, expect, test } from "bun:test";
|
||||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
import { mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { parse } from "@wrnexus/syntax";
|
import { parse } from "@wrnexus/syntax";
|
||||||
import { generate } from "../src/codegen.ts";
|
import { generate } from "../src/codegen.ts";
|
||||||
|
|
||||||
|
// The generated module dynamically imported below is written to an OS
|
||||||
|
// tmpdir with no node_modules of its own, so Node's bare-specifier
|
||||||
|
// resolution for "@wrnexus/core" would otherwise walk up to whatever
|
||||||
|
// (possibly stale, globally-installed) copy happens to sit outside the
|
||||||
|
// workspace. Symlink the workspace package in so it resolves to the real,
|
||||||
|
// currently-built `@wrnexus/core` — the same one every other package in
|
||||||
|
// this repo gets via its own `node_modules/@wrnexus/core` symlink.
|
||||||
|
const WORKSPACE_CORE = join(import.meta.dir, "../../core");
|
||||||
|
|
||||||
|
function linkWorkspaceCore(root: string): void {
|
||||||
|
const scopeDir = join(root, "node_modules", "@wrnexus");
|
||||||
|
mkdirSync(scopeDir, { recursive: true });
|
||||||
|
symlinkSync(
|
||||||
|
WORKSPACE_CORE,
|
||||||
|
join(scopeDir, "core"),
|
||||||
|
process.platform === "win32" ? "junction" : "dir",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const ROOT_TSCONFIG = join(import.meta.dir, "../../../tsconfig.json").replace(/\\/g, "/");
|
const ROOT_TSCONFIG = join(import.meta.dir, "../../../tsconfig.json").replace(/\\/g, "/");
|
||||||
// The repo's own tsc, not a `bunx`-fetched one — `bunx tsc` can resolve an
|
// The repo's own tsc, not a `bunx`-fetched one — `bunx tsc` can resolve an
|
||||||
// unrelated TypeScript version that doesn't understand this repo's tsconfig
|
// unrelated TypeScript version that doesn't understand this repo's tsconfig
|
||||||
@@ -150,6 +169,7 @@ test("an ssr block used in {#each} with an error section runs the error body on
|
|||||||
const root = mkdtempSync(join(tmpdir(), "wrnexus-ssr-each-"));
|
const root = mkdtempSync(join(tmpdir(), "wrnexus-ssr-each-"));
|
||||||
roots.push(root);
|
roots.push(root);
|
||||||
mkdirSync(root, { recursive: true });
|
mkdirSync(root, { recursive: true });
|
||||||
|
linkWorkspaceCore(root);
|
||||||
const file = join(root, "page.ts");
|
const file = join(root, "page.ts");
|
||||||
writeFileSync(file, generated);
|
writeFileSync(file, generated);
|
||||||
|
|
||||||
@@ -189,6 +209,7 @@ test("an ssr block's response body error is not swallowed by the error section",
|
|||||||
const root = mkdtempSync(join(tmpdir(), "wrnexus-ssr-response-throws-"));
|
const root = mkdtempSync(join(tmpdir(), "wrnexus-ssr-response-throws-"));
|
||||||
roots.push(root);
|
roots.push(root);
|
||||||
mkdirSync(root, { recursive: true });
|
mkdirSync(root, { recursive: true });
|
||||||
|
linkWorkspaceCore(root);
|
||||||
const file = join(root, "page.ts");
|
const file = join(root, "page.ts");
|
||||||
writeFileSync(file, generated);
|
writeFileSync(file, generated);
|
||||||
|
|
||||||
@@ -227,6 +248,7 @@ test("an ssr block still runs the error body on a genuine transport failure", as
|
|||||||
const root = mkdtempSync(join(tmpdir(), "wrnexus-ssr-transport-fails-"));
|
const root = mkdtempSync(join(tmpdir(), "wrnexus-ssr-transport-fails-"));
|
||||||
roots.push(root);
|
roots.push(root);
|
||||||
mkdirSync(root, { recursive: true });
|
mkdirSync(root, { recursive: true });
|
||||||
|
linkWorkspaceCore(root);
|
||||||
const file = join(root, "page.ts");
|
const file = join(root, "page.ts");
|
||||||
writeFileSync(file, generated);
|
writeFileSync(file, generated);
|
||||||
|
|
||||||
@@ -263,6 +285,7 @@ test("an ssr block used in {#each} without an error section still propagates a f
|
|||||||
const root = mkdtempSync(join(tmpdir(), "wrnexus-ssr-each-propagate-"));
|
const root = mkdtempSync(join(tmpdir(), "wrnexus-ssr-each-propagate-"));
|
||||||
roots.push(root);
|
roots.push(root);
|
||||||
mkdirSync(root, { recursive: true });
|
mkdirSync(root, { recursive: true });
|
||||||
|
linkWorkspaceCore(root);
|
||||||
const file = join(root, "page.ts");
|
const file = join(root, "page.ts");
|
||||||
writeFileSync(file, generated);
|
writeFileSync(file, generated);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { test, expect } from "bun:test";
|
import { test, expect } from "bun:test";
|
||||||
import { writeFileSync, mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
import { writeFileSync, mkdirSync, mkdtempSync, rmSync, symlinkSync, existsSync } from "node:fs";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { pathToFileURL } from "node:url";
|
import { pathToFileURL } from "node:url";
|
||||||
@@ -7,6 +7,23 @@ import { generate, parse } from "../src/index.ts";
|
|||||||
import { compileWrnFile } from "../src/index.ts";
|
import { compileWrnFile } from "../src/index.ts";
|
||||||
import { mountHtml } from "@wrnexus/test";
|
import { mountHtml } from "@wrnexus/test";
|
||||||
|
|
||||||
|
// The compiled module below is written to an OS tmpdir with no node_modules
|
||||||
|
// of its own, so Node's bare-specifier resolution for "@wrnexus/core" would
|
||||||
|
// otherwise walk up to whatever (possibly stale, globally-installed) copy
|
||||||
|
// happens to sit outside the workspace. Symlink the workspace package in so
|
||||||
|
// it resolves to the real, currently-built `@wrnexus/core` — the same one
|
||||||
|
// every other package in this repo gets via its own
|
||||||
|
// `node_modules/@wrnexus/core` symlink.
|
||||||
|
const WORKSPACE_CORE = join(import.meta.dir, "../../core");
|
||||||
|
|
||||||
|
function linkWorkspaceCore(root: string): void {
|
||||||
|
const scopeDir = join(root, "node_modules", "@wrnexus");
|
||||||
|
const linkPath = join(scopeDir, "core");
|
||||||
|
if (existsSync(linkPath)) return;
|
||||||
|
mkdirSync(scopeDir, { recursive: true });
|
||||||
|
symlinkSync(WORKSPACE_CORE, linkPath, process.platform === "win32" ? "junction" : "dir");
|
||||||
|
}
|
||||||
|
|
||||||
test("explicit static rendering disables hydration metadata", () => {
|
test("explicit static rendering disables hydration metadata", () => {
|
||||||
const output = compileWrnFile(`page StaticPage {
|
const output = compileWrnFile(`page StaticPage {
|
||||||
render = "static"
|
render = "static"
|
||||||
@@ -33,6 +50,7 @@ let seq = 0;
|
|||||||
async function compileAndImport(src: string): Promise<Record<string, unknown>> {
|
async function compileAndImport(src: string): Promise<Record<string, unknown>> {
|
||||||
const dir = join(tmpdir(), "wrn-compiler-test");
|
const dir = join(tmpdir(), "wrn-compiler-test");
|
||||||
mkdirSync(dir, { recursive: true });
|
mkdirSync(dir, { recursive: true });
|
||||||
|
linkWorkspaceCore(dir);
|
||||||
const file = join(dir, `m${seq++}.ts`);
|
const file = join(dir, `m${seq++}.ts`);
|
||||||
writeFileSync(file, compileWrnFile(src));
|
writeFileSync(file, compileWrnFile(src));
|
||||||
return import(pathToFileURL(file).href);
|
return import(pathToFileURL(file).href);
|
||||||
@@ -1351,6 +1369,7 @@ test("page state SSR condition renders without ReferenceError", async () => {
|
|||||||
const generatedFile = join(root, "pricing.generated.ts");
|
const generatedFile = join(root, "pricing.generated.ts");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
linkWorkspaceCore(root);
|
||||||
const source = `
|
const source = `
|
||||||
page Pricing {
|
page Pricing {
|
||||||
state billingCycle = "monthly"
|
state billingCycle = "monthly"
|
||||||
|
|||||||
Reference in New Issue
Block a user