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
5 changes: 5 additions & 0 deletions .changeset/name-router-primitives.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/router": patch
---

Dev builds name the data layer's primitives after their keys (`query:<key>`, `liveQuery.status:<key>`) and the router's location, url and submissions nodes (`router.*`), so attribution diagnostics say which query is waiting instead of `"signal"`. The names sit behind `DEV &&` and fold out of production bundles.
5 changes: 4 additions & 1 deletion src/data/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,10 @@ export function useSubmissions<T extends Array<any>, U, V>(
router.submissions[0]().filter(
s => s.url === (fn as any).base && (!filter || filter(s.input))
),
{ equals: (a, b) => a.length === b.length && a.every((s, i) => s === b[i]) }
{
equals: (a, b) => a.length === b.length && a.every((s, i) => s === b[i]),
...(DEV && { name: "router.submissions" })
}
);
return new Proxy<Submission<any, any>[]>([] as any, {
get(_, property) {
Expand Down
10 changes: 9 additions & 1 deletion src/data/liveQuery.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createSignal, type Signal } from "solid-js";
import { DEV } from "solid-js";
import {
getRequestEvent,
getServerFunctionMetadata,
Expand Down Expand Up @@ -68,7 +69,14 @@ function statusSignal(key: string) {
// ownedWrite: the first write happens synchronously inside whatever
// computation pulled the channel open — this is channel-owned state, not
// the computation's own.
if (!s) statusMap.set(key, (s = createSignal<LiveQueryStatus>("idle", { ownedWrite: true })));
if (!s)
statusMap.set(
key,
(s = createSignal<LiveQueryStatus>("idle", {
ownedWrite: true,
...(DEV && { name: `liveQuery.status:${key}` })
}))
);
return s;
}

Expand Down
11 changes: 9 additions & 2 deletions src/data/query.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DEV } from "solid-js";
import {
createSignal,
getObserver,
Expand Down Expand Up @@ -319,7 +320,10 @@ export function query<T extends (...args: any) => any>(fn: T, name: string): Cac
res,
,
intent,
createSignal(stamp, { ownedWrite: true }) as Signal<number> & { count: number }
createSignal(stamp, {
ownedWrite: true,
...(DEV && { name: `query:${key}` })
}) as Signal<number> & { count: number }
])
);
cached[4].count = 0;
Expand Down Expand Up @@ -434,7 +438,10 @@ query.set = <T>(key: string, value: T extends Promise<any> ? never : T) => {
Promise.resolve(value),
value,
"preload",
createSignal(now, { ownedWrite: true }) as Signal<number> & { count: number }
createSignal(now, {
ownedWrite: true,
...(DEV && { name: `query:${key}` })
}) as Signal<number> & { count: number }
])
);
cached[4].count = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/routers/factory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ function createIntegration(
const [read, write] = createSignal(wrap(history.get()), {
equals: (a, b) => a.value === b.value && a.state === b.state && a._navigation === b._navigation,
ownedWrite: true,
name: "location"
...(DEV && { name: "router.location" })
});
const signal: RouterIntegration["signal"] = [
read,
Expand Down
3 changes: 2 additions & 1 deletion src/routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,8 @@ function createLocation(
}
},
{
equals: (a, b) => a.href === b.href
equals: (a, b) => a.href === b.href,
...(DEV && { name: "router.url" })
}
);

Expand Down
28 changes: 27 additions & 1 deletion test/data/live-query.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { vi } from "vitest";
import { flush } from "solid-js";
import { DEV, createRoot, flush } from "solid-js";
import {
createServerReference,
getServerFunctionMetadata,
Expand Down Expand Up @@ -648,3 +648,29 @@ describe("hydration takeover resume", () => {
await it.return!();
});
});

// Dev builds name the data layer's primitives after their keys, so
// attribution diagnostics (SILENT_HOLD, waterfalls) say which query is
// waiting instead of "signal". Production folds the names out (`DEV &&`).
function namesCreatedBy(run: () => void): string[] {
const names: string[] = [];
DEV!.hooks.onGraph = (node: any) => node?._name && names.push(node._name);
try {
createRoot(run);
} finally {
DEV!.hooks.onGraph = undefined;
}
return names;
}

describe("liveQuery primitive names", () => {
test("the status signal carries the channel key", () => {
const lq = liveQuery(async function* () {
yield 1;
}, uniqueName());
const names = namesCreatedBy(() => {
lq.status();
});
expect(names).toContain(`liveQuery.status:${lq.keyFor()}`);
});
});
26 changes: 25 additions & 1 deletion test/data/query.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createRoot } from "solid-js";
import { DEV, createRoot } from "solid-js";
import { vi } from "vitest";
import {
GET,
Expand Down Expand Up @@ -623,3 +623,27 @@ describe("hashKey should", () => {
);
});
});

// Dev builds name the data layer's primitives after their keys, so
// attribution diagnostics (SILENT_HOLD, waterfalls) say which query is
// waiting instead of "signal". Production folds the names out (`DEV &&`).
function namesCreatedBy(run: () => void): string[] {
const names: string[] = [];
DEV!.hooks.onGraph = (node: any) => node?._name && names.push(node._name);
try {
createRoot(run);
} finally {
DEV!.hooks.onGraph = undefined;
}
return names;
}

describe("query primitive names", () => {
test("a query's version signal carries its key", () => {
const q = query(async (id: string) => id, "names-query");
const names = namesCreatedBy(() => {
q("a");
});
expect(names).toContain(`query:${q.keyFor("a")}`);
});
});
87 changes: 87 additions & 0 deletions test/navigate-commit.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,93 @@ describe("navigateFromRoute never drops the last navigation", () => {
}
});

test("Forward during a pending Back lands on the Forward target (#687)", async () => {
const history = memoryHistory();
history.set({ value: "/slow" });
history.set({ value: "/fast" });

let releaseSlow!: () => void;
let gate = Promise.resolve();
const routes = [
{ path: "/", component: page("home") },
{
path: "/slow",
component: () => {
const value = createMemo(async () => {
await gate;
return "slow";
});
return <div>{value()}</div>;
}
},
{ path: "/fast", component: page("fast") }
] as const;
const app = mount(routes, history);
try {
await settle();
expect(app.text()).toBe("fast");
gate = new Promise<void>(resolve => (releaseSlow = resolve));

history.back();
await settle();
// held on /slow's data: still showing /fast
expect(app.text()).toBe("fast");

history.forward();
await settle();
releaseSlow();
await settle();

expect(history.get()).toBe("/fast");
expect(app.text()).toBe("fast");
} finally {
app.cleanup();
}
});

test("a navigation during a held one lands on the newer target (#687)", async () => {
const history = memoryHistory();
let navigate!: Navigator;
let releaseSlow!: () => void;
const gate = new Promise<void>(resolve => (releaseSlow = resolve));
const routes = [
{
path: "/",
component: () => {
navigate = useNavigate();
return <div>home</div>;
}
},
{
path: "/slow",
component: () => {
const value = createMemo(async () => {
await gate;
return "slow";
});
return <div>{value()}</div>;
}
},
{ path: "/fast", component: page("fast") }
] as const;
const app = mount(routes, history);
try {
navigate("/slow");
await settle();
expect(app.text()).toBe("home");

navigate("/fast");
await settle();
releaseSlow();
await settle();

expect(history.get()).toBe("/fast");
expect(app.text()).toBe("fast");
} finally {
app.cleanup();
}
});

test("navigation intent is derived from the pending source transition", async () => {
const history = memoryHistory();
let navigate!: Navigator;
Expand Down
Loading