refactor: replace the legacy function runtime with shared

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 23:13:05 +05:30
co-authored by Claude Opus 5
parent 224af8fd96
commit ec63090006
10 changed files with 72 additions and 33 deletions
+2 -6
View File
@@ -169,9 +169,7 @@ function selectedBrowserImports(
}
export function browserModuleRequired(ast: PageAst): boolean {
const functions = ast.runtimeFunctions.filter((fn) =>
["legacy", "client", "shared"].includes(fn.runtime),
);
const functions = ast.runtimeFunctions.filter((fn) => ["client", "shared"].includes(fn.runtime));
return functions.length > 0 || selectedBrowserImports(ast, functions).length > 0;
}
@@ -336,9 +334,7 @@ function apiBindings(ast: PageAst): string {
}
export function generateBrowserModule(ast: PageAst): string {
const functions = ast.runtimeFunctions.filter((fn) =>
["legacy", "client", "shared"].includes(fn.runtime),
);
const functions = ast.runtimeFunctions.filter((fn) => ["client", "shared"].includes(fn.runtime));
const functionNames = functions.map((fn) => fn.name);
const state = ast.states.filter((entry) => entry.runtime !== "server").map((entry) => entry.name);
const selectedImports = selectedBrowserImports(ast, functions);
+1 -3
View File
@@ -1310,9 +1310,7 @@ function hydrationAttribute(ast: PageAst): string {
function targetFunctions(ast: PageAst, target: "browser" | "server"): string {
const runtimes =
target === "browser"
? (["legacy", "client", "shared"] as const)
: (["legacy", "server", "shared"] as const);
target === "browser" ? (["client", "shared"] as const) : (["server", "shared"] as const);
return ast.functions
.map((body) => stripRuntimeFunctionModifiers(body, [...runtimes]))
.map((body) => body.trim())
+3 -3
View File
@@ -25,7 +25,7 @@ function stableId(value: string): string {
*/
export function remotelyReferencedServerFunctions(ast: PageAst): Set<string> {
const browserSources = ast.runtimeFunctions
.filter((fn) => ["legacy", "client", "shared"].includes(fn.runtime))
.filter((fn) => ["client", "shared"].includes(fn.runtime))
.map((fn) => fn.body);
for (const [hook, body] of Object.entries(ast.storeLifecycle)) {
if (hook !== "serverInit" && body) browserSources.push(body);
@@ -57,11 +57,11 @@ export function rpcManifest(ast: PageAst): RpcManifestEntry[] {
export function generateServerFunctionsModule(ast: PageAst): string {
const source = ast.functions
.map((body) => stripRuntimeFunctionModifiers(body, ["legacy", "server", "shared"]))
.map((body) => stripRuntimeFunctionModifiers(body, ["server", "shared"]))
.filter(Boolean)
.join("\n\n");
const names = ast.runtimeFunctions
.filter((fn) => ["legacy", "server", "shared"].includes(fn.runtime))
.filter((fn) => ["server", "shared"].includes(fn.runtime))
.map((fn) => fn.name);
const manifest = rpcManifest(ast);
return `// generated WRNexusJS server module for ${ast.name}\n${source}\n\nexport const __wrnexusServerFunctions = { ${[...new Set(names)].join(", ")} };\nexport const __wrnexusRpcManifest = ${JSON.stringify(manifest, null, 2)};\n`;
+2 -2
View File
@@ -175,7 +175,7 @@ export function generateStoreBrowserModule(ast: PageAst): string {
.join(",\n");
const groups = new Map<string, RuntimeFunctionDecl[]>();
for (const fn of ast.runtimeFunctions.filter((entry) =>
["client", "shared", "legacy"].includes(entry.runtime),
["client", "shared"].includes(entry.runtime),
)) {
const group = groups.get(fn.name) ?? [];
group.push(fn);
@@ -296,7 +296,7 @@ function __create(definition) {
Object.keys(actions).forEach(function (name) { delete actions[name]; });
Object.entries(currentDefinition.actions || {}).forEach(function (pair) {
const name = pair[0], candidates = pair[1];
const selected = candidates.find(function (entry) { return entry.runtime === "client"; }) || candidates.find(function (entry) { return entry.runtime === "shared"; }) || candidates.find(function (entry) { return entry.runtime === "legacy"; });
const selected = candidates.find(function (entry) { return entry.runtime === "client"; }) || candidates.find(function (entry) { return entry.runtime === "shared"; });
if (!selected) return;
actions[name] = async function () {
const args = Array.prototype.slice.call(arguments);
+1 -1
View File
@@ -47,7 +47,7 @@ export function generateDeclarations(ast: PageAst): string {
)
.join("\n");
const clientFunctions = ast.runtimeFunctions
.filter((fn) => fn.runtime === "client" || fn.runtime === "shared" || fn.runtime === "legacy")
.filter((fn) => fn.runtime === "client" || fn.runtime === "shared")
.map(
(fn) =>
` ${member(fn.name)}(${params(fn.parameters)}): ${fn.returnType ?? (fn.async ? "Promise<unknown>" : "unknown")};`,
@@ -0,0 +1,47 @@
import { expect, test } from "bun:test";
import { parse } from "@wrnexus/syntax";
import { generateTargets } from "../src/targets.ts";
const SOURCE = `page Probe {
functions {
function unmarkedHelper() {
return "both";
}
client function clientOnly() {
return "browser";
}
server function serverOnly() {
return "server";
}
}
view { <main>x</main> }
}
`;
test("an unmarked function is emitted into both the browser and server modules", () => {
// This is the property the "legacy" runtime provided. Removing the variant
// must not change it.
const targets = generateTargets(parse(SOURCE));
expect(targets.browser).toContain("unmarkedHelper");
expect(targets.server).toContain("unmarkedHelper");
});
test("marked functions still go only where they belong", () => {
const targets = generateTargets(parse(SOURCE));
expect(targets.browser).toContain("clientOnly");
expect(targets.browser).not.toContain("serverOnly");
expect(targets.server).toContain("serverOnly");
expect(targets.server).not.toContain("clientOnly");
});
test("no emitted target mentions the removed legacy runtime", () => {
const targets = generateTargets(parse(SOURCE));
expect(targets.browser).not.toContain('"legacy"');
expect(targets.server).not.toContain('"legacy"');
});
+2 -2
View File
@@ -1,6 +1,6 @@
import { Lexer, LexError } from "./tokenizer.ts";
export type FunctionRuntime = "legacy" | "client" | "server" | "shared";
export type FunctionRuntime = "client" | "server" | "shared";
export type StateRuntime = "shared" | "client" | "server";
export type StoreKind = "global" | "page";
@@ -206,7 +206,7 @@ export function parseRuntimeFunctions(source: string): RuntimeFunctionDecl[] {
i++;
continue;
}
let runtime: FunctionRuntime = "legacy";
let runtime: FunctionRuntime = "shared";
if (["client", "server", "shared"].includes(token.word)) {
runtime = token.word as FunctionRuntime;
i = skipTrivia(source, token.end);