Skip to content

Commit f3c55e8

Browse files
authored
feat: add createContext utility for type-safe context (#16948)
* feat: add `createContext` utility for type-safe context * regenerate
1 parent 99711d5 commit f3c55e8

File tree

12 files changed

+110
-2
lines changed

12 files changed

+110
-2
lines changed

.changeset/neat-melons-cheer.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': minor
3+
---
4+
5+
feat: add `createContext` utility for type-safe context

documentation/docs/98-reference/.generated/shared-errors.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ Certain lifecycle methods can only be used during component initialisation. To f
6060
<button onclick={handleClick}>click me</button>
6161
```
6262

63+
### missing_context
64+
65+
```
66+
Context was not set in a parent component
67+
```
68+
69+
The [`createContext()`](svelte#createContext) utility returns a `[get, set]` pair of functions. `get` will throw an error if `set` was not used to set the context in a parent component.
70+
6371
### snippet_without_render_tag
6472

6573
```

packages/svelte/messages/shared-errors/errors.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ Certain lifecycle methods can only be used during component initialisation. To f
5252
<button onclick={handleClick}>click me</button>
5353
```
5454

55+
## missing_context
56+
57+
> Context was not set in a parent component
58+
59+
The [`createContext()`](svelte#createContext) utility returns a `[get, set]` pair of functions. `get` will throw an error if `set` was not used to set the context in a parent component.
60+
5561
## snippet_without_render_tag
5662

5763
> Attempted to render a snippet without a `{@render}` block. This would cause the snippet code to be stringified instead of its content being rendered to the DOM. To fix this, change `{snippet}` to `{@render snippet()}`.

packages/svelte/src/index-client.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,13 @@ function init_update_callbacks(context) {
242242
}
243243

244244
export { flushSync } from './internal/client/reactivity/batch.js';
245-
export { getContext, getAllContexts, hasContext, setContext } from './internal/client/context.js';
245+
export {
246+
createContext,
247+
getContext,
248+
getAllContexts,
249+
hasContext,
250+
setContext
251+
} from './internal/client/context.js';
246252
export { hydrate, mount, unmount } from './internal/client/render.js';
247253
export { tick, untrack, settled } from './internal/client/runtime.js';
248254
export { createRawSnippet } from './internal/client/dom/blocks/snippet.js';

packages/svelte/src/index-server.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ export async function settled() {}
3939

4040
export { getAbortSignal } from './internal/server/abort-signal.js';
4141

42-
export { getAllContexts, getContext, hasContext, setContext } from './internal/server/context.js';
42+
export {
43+
createContext,
44+
getAllContexts,
45+
getContext,
46+
hasContext,
47+
setContext
48+
} from './internal/server/context.js';
4349

4450
export { createRawSnippet } from './internal/server/blocks/snippet.js';

packages/svelte/src/internal/client/context.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,26 @@ export function set_dev_current_component_function(fn) {
6969
dev_current_component_function = fn;
7070
}
7171

72+
/**
73+
* Returns a `[get, set]` pair of functions for working with context in a type-safe way.
74+
* @template T
75+
* @returns {[() => T, (context: T) => T]}
76+
*/
77+
export function createContext() {
78+
const key = {};
79+
80+
return [
81+
() => {
82+
if (!hasContext(key)) {
83+
e.missing_context();
84+
}
85+
86+
return getContext(key);
87+
},
88+
(context) => setContext(key, context)
89+
];
90+
}
91+
7292
/**
7393
* Retrieves the context that belongs to the closest parent component with the specified `key`.
7494
* Must be called during component initialisation.

packages/svelte/src/internal/server/context.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ export function set_ssr_context(v) {
1010
ssr_context = v;
1111
}
1212

13+
/**
14+
* @template T
15+
* @returns {[() => T, (context: T) => T]}
16+
*/
17+
export function createContext() {
18+
const key = {};
19+
return [() => getContext(key), (context) => setContext(key, context)];
20+
}
21+
1322
/**
1423
* @template T
1524
* @param {any} key

packages/svelte/src/internal/shared/errors.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,22 @@ export function lifecycle_outside_component(name) {
5151
}
5252
}
5353

54+
/**
55+
* Context was not set in a parent component
56+
* @returns {never}
57+
*/
58+
export function missing_context() {
59+
if (DEV) {
60+
const error = new Error(`missing_context\nContext was not set in a parent component\nhttps://svelte.dev/e/missing_context`);
61+
62+
error.name = 'Svelte error';
63+
64+
throw error;
65+
} else {
66+
throw new Error(`https://svelte.dev/e/missing_context`);
67+
}
68+
}
69+
5470
/**
5571
* Attempted to render a snippet without a `{@render}` block. This would cause the snippet code to be stringified instead of its content being rendered to the DOM. To fix this, change `{snippet}` to `{@render snippet()}`.
5672
* @returns {never}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
import { get } from './main.svelte';
3+
4+
const message = get();
5+
</script>
6+
7+
<h1>{message}</h1>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
html: `<h1>hello</h1>`
5+
});

0 commit comments

Comments
 (0)