Skip to content

Commit

Permalink
feat: add untrack to load functions
Browse files Browse the repository at this point in the history
closes #6294
  • Loading branch information
dummdidumm committed Dec 14, 2023
1 parent a00183a commit c624d8c
Show file tree
Hide file tree
Showing 12 changed files with 189 additions and 13 deletions.
5 changes: 5 additions & 0 deletions .changeset/fast-eyes-deny.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@sveltejs/kit": minor
---

feat: add untrack to load
28 changes: 28 additions & 0 deletions packages/kit/src/exports/public.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,20 @@ export interface LoadEvent<
* ```
*/
depends(...deps: Array<`${string}:${string}`>): void;
/**
* Use this function to opt out of dependency tracking for everything that is synchronously called within the callback. Example:
*
* ```js
* /// file: src/routes/+page.server.js
* export async function load({ untrack, url }) {
* // Untrack url.pathname so that path changes don't trigger a rerun
* if (untrack(() => url.pathname === '/')) {
* return { message: 'Welcome!' };
* }
* }
* ```
*/
untrack<T>(fn: () => T): T;
}

export interface NavigationEvent<
Expand Down Expand Up @@ -1192,6 +1206,20 @@ export interface ServerLoadEvent<
* ```
*/
depends(...deps: string[]): void;
/**
* Use this function to opt out of dependency tracking for everything that is synchronously called within the callback. Example:
*
* ```js
* /// file: src/routes/+page.js
* export async function load({ untrack, url }) {
* // Untrack url.pathname so that path changes don't trigger a rerun
* if (untrack(() => url.pathname === '/')) {
* return { message: 'Welcome!' };
* }
* }
* ```
*/
untrack<T>(fn: () => T): T;
}

/**
Expand Down
38 changes: 32 additions & 6 deletions packages/kit/src/runtime/client/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,8 @@ export function create_client(app, target) {
/** @type {Record<string, any> | null} */
let data = null;

let is_tracking = true;

/** @type {import('types').Uses} */
const uses = {
dependencies: new Set(),
Expand Down Expand Up @@ -465,21 +467,33 @@ export function create_client(app, target) {
const load_input = {
route: new Proxy(route, {
get: (target, key) => {
uses.route = true;
if (is_tracking) {
uses.route = true;
}
return target[/** @type {'id'} */ (key)];
}
}),
params: new Proxy(params, {
get: (target, key) => {
uses.params.add(/** @type {string} */ (key));
if (is_tracking) {
uses.params.add(/** @type {string} */ (key));
}
return target[/** @type {string} */ (key)];
}
}),
data: server_data_node?.data ?? null,
url: make_trackable(
url,
() => (uses.url = true),
(param) => uses.search_params.add(param)
() => {
if (is_tracking) {
uses.url = true;
}
},
(param) => {
if (is_tracking) {
uses.search_params.add(param);
}
}
),
async fetch(resource, init) {
/** @type {URL | string} */
Expand Down Expand Up @@ -516,7 +530,9 @@ export function create_client(app, target) {

// we must fixup relative urls so they are resolved from the target page
const resolved = new URL(requested, url);
depends(resolved.href);
if (is_tracking) {
depends(resolved.href);
}

// match ssr serialized data url, which is important to find cached responses
if (resolved.origin === url.origin) {
Expand All @@ -531,8 +547,18 @@ export function create_client(app, target) {
setHeaders: () => {}, // noop
depends,
parent() {
uses.parent = true;
if (is_tracking) {
uses.parent = true;
}
return parent();
},
untrack(fn) {
is_tracking = false;
try {
return fn();
} finally {
is_tracking = true;
}
}
};

Expand Down
35 changes: 28 additions & 7 deletions packages/kit/src/runtime/server/page/load_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export async function load_server_data({ event, state, node, parent }) {
if (!node?.server) return null;

let done = false;
let is_tracking = true;

const uses = {
dependencies: new Set(),
Expand All @@ -35,7 +36,9 @@ export async function load_server_data({ event, state, node, parent }) {
);
}

uses.url = true;
if (is_tracking) {
uses.url = true;
}
},
(param) => {
if (DEV && done && !uses.search_params.has(param)) {
Expand All @@ -44,7 +47,9 @@ export async function load_server_data({ event, state, node, parent }) {
);
}

uses.search_params.add(param);
if (is_tracking) {
uses.search_params.add(param);
}
}
);

Expand All @@ -63,6 +68,7 @@ export async function load_server_data({ event, state, node, parent }) {
);
}

// Note: server fetches are not added to uses.depends due to security concerns
return event.fetch(info, init);
},
/** @param {string[]} deps */
Expand Down Expand Up @@ -93,7 +99,9 @@ export async function load_server_data({ event, state, node, parent }) {
);
}

uses.params.add(key);
if (is_tracking) {
uses.params.add(key);
}
return target[/** @type {string} */ (key)];
}
}),
Expand All @@ -104,7 +112,9 @@ export async function load_server_data({ event, state, node, parent }) {
);
}

uses.parent = true;
if (is_tracking) {
uses.parent = true;
}
return parent();
},
route: new Proxy(event.route, {
Expand All @@ -117,11 +127,21 @@ export async function load_server_data({ event, state, node, parent }) {
);
}

uses.route = true;
if (is_tracking) {
uses.route = true;
}
return target[/** @type {'id'} */ (key)];
}
}),
url
url,
untrack(fn) {
is_tracking = false;
try {
return fn();
} finally {
is_tracking = true;
}
}
});

if (__SVELTEKIT_DEV__) {
Expand Down Expand Up @@ -176,7 +196,8 @@ export async function load_data({
fetch: create_universal_fetch(event, state, fetched, csr, resolve_opts),
setHeaders: event.setHeaders,
depends: () => {},
parent
parent,
untrack: (fn) => fn()
});

if (__SVELTEKIT_DEV__) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function load({ url }) {
return {
url: url.pathname
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export function load({ params, parent, url, untrack }) {
untrack(() => {
params.x;
parent();
url.pathname;
url.search;
});

return {
id: Math.random()
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script>
export let data;
</script>

<p class="url">{data.url}</p>
<p class="id">{data.id}</p>
<a href="/untrack/server/2">2</a>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function load({ url }) {
return {
url: url.pathname
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export function load({ params, parent, url, untrack }) {
untrack(() => {
params.x;
parent();
url.pathname;
url.search;
});

return {
id: Math.random()
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script>
export let data;
</script>

<p class="url">{data.url}</p>
<p class="id">{data.id}</p>
<a href="/untrack/universal/2">2</a>
20 changes: 20 additions & 0 deletions packages/kit/test/apps/basics/test/client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -913,3 +913,23 @@ test.describe('goto', () => {
await expect(page.locator('p')).toHaveText(message);
});
});

test.describe('untrack', () => {
test('untracks server load function', async ({ page }) => {
await page.goto('/untrack/server/1');
expect(await page.textContent('p.url')).toBe('/untrack/server/1');
const id = await page.textContent('p.id');
await page.click('a[href="/untrack/server/2"]');
expect(await page.textContent('p.url')).toBe('/untrack/server/2');
expect(await page.textContent('p.id')).toBe(id);
});

test('untracks universal load function', async ({ page }) => {
await page.goto('/untrack/universal/1');
expect(await page.textContent('p.url')).toBe('/untrack/universal/1');
const id = await page.textContent('p.id');
await page.click('a[href="/untrack/universal/2"]');
expect(await page.textContent('p.url')).toBe('/untrack/universal/2');
expect(await page.textContent('p.id')).toBe(id);
});
});
28 changes: 28 additions & 0 deletions packages/kit/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,20 @@ declare module '@sveltejs/kit' {
* ```
*/
depends(...deps: Array<`${string}:${string}`>): void;
/**
* Use this function to opt out of dependency tracking for everything that is synchronously called within the callback. Example:
*
* ```js
* /// file: src/routes/+page.server.js
* export async function load({ untrack, url }) {
* // Untrack url.pathname so that path changes don't trigger a rerun
* if (untrack(() => url.pathname === '/')) {
* return { message: 'Welcome!' };
* }
* }
* ```
*/
untrack<T>(fn: () => T): T;
}

export interface NavigationEvent<
Expand Down Expand Up @@ -1174,6 +1188,20 @@ declare module '@sveltejs/kit' {
* ```
*/
depends(...deps: string[]): void;
/**
* Use this function to opt out of dependency tracking for everything that is synchronously called within the callback. Example:
*
* ```js
* /// file: src/routes/+page.js
* export async function load({ untrack, url }) {
* // Untrack url.pathname so that path changes don't trigger a rerun
* if (untrack(() => url.pathname === '/')) {
* return { message: 'Welcome!' };
* }
* }
* ```
*/
untrack<T>(fn: () => T): T;
}

/**
Expand Down

0 comments on commit c624d8c

Please sign in to comment.