Skip to content

Commit

Permalink
feat(svelte-query): Svelte Query Adapter for TanStack Query (#4768)
Browse files Browse the repository at this point in the history
* feat: svelte-query adapter

* add examples

* add @testing-library/svelte and svelte-jester

* add transform for testing svelte components

* re-export @tanstack/core core

* add a few initial test

* delete tests in examples

* use ^ in dependencies

* delete and gitignore .vscode

* rename basic-typescript to basic

* add basic example to ci.json

* remove transform from global preset

* update version

* don't gitignore lib directory in svelte examples

* fix build files location in package.json

* chore: prettier formatting

* add missing state

* chore: fix eslint errors

* add context

* unsubscribe

* add missing export

* use svelte context

* update version

* update examples to use context

* release: v4.14.5

* remove onMount

* add onMount to setQueryClient

* remove unneeded exports

* Add basic svelte docs overview

* Add SvelteKit 1.0 example

Uses the data from the simple example

* Edit nodeResolve settings

* More rollup and babel tweaks

Seems to be working

* Try svelte plugin options

* Separate out svelte config

* Switch to svelte-package

* Reset rollup config

* Output to ./build

* Use vitest in svelte-query

* Fix test imports

* Avoid transpiling TS during typecheck

* Fix vitest command

* More vitest migration work

* Minor fixes to tests

* Rename file to types.d.ts

* Replace setQueryContext with QueryClientProvider

* Replace tabs with spaces for repo consistency

* Update examples to sveltekit 1.0

* Implement Hydrate component

* Add playground example

Rewrite of the react example

* Start adding star-wars example

* Add films and film pages

* Finish star-wars example

* Rename store functions with create prefix

* Add correct favicons

* Reorder some files in svelte-query

* Undo rollup.config.ts changes

* Add new createQuery and createMutation tests

* More descriptive test name

* Misc fixes from feedback

- Max TS 4.7.4
- Move root package.json dependencies
- Use object syntax
- Use test:jest script (for now)
- Remove sveltekit autogenerated files

* Fix pnpm-lock

* Specify svelte-package source, update TS to 4.7.4

* Remove unnecessary packages in examples

* Sync pnpm-lock

* Reset pnpm-lock to upstream

* Run pnpm install

* Remove svelte-query from root tsconfg

Needs to be run from its own svelte-check package... sorry

* Run prettier, remove unused import

* Run prettier on svelte files

Requires prettier-plugin-svelte to run

* Prettier for changed files

* Fix cipublish from svelte-query

* rimraf to remove build/lib/package.json

* Run svelte-kit sync before vitest

* Add files field to package.json

* Add types field to package.json

* Bump svelte-query version to 4.20.0

In case this fixes the CI issue

* Add path alias to vitest

* Update vitest to 0.26

Changes module resolution?

* More CI improvements

* Remove --parallel from pnpm scripts

* Remove unused dependency

* Add eslint settings

Thanks @rivo420

* Add coverage report

Co-authored-by: DeAndre Johnson <dre@phreshr.com>
Co-authored-by: Dominik Dorfmeister <office@dorfmeister.cc>

RELEASE_ALL
  • Loading branch information
lachlancollins committed Jan 8, 2023
1 parent 85ce6db commit b324a9b
Show file tree
Hide file tree
Showing 169 changed files with 6,295 additions and 107 deletions.
2 changes: 1 addition & 1 deletion .codesandbox/ci.json
@@ -1,6 +1,6 @@
{
"installCommand": "install:csb",
"sandboxes": ["/examples/react/basic-typescript", "/examples/solid/basic-typescript", "/examples/vue/basic"],
"sandboxes": ["/examples/react/basic-typescript", "/examples/solid/basic-typescript", "/examples/svelte/basic", "/examples/vue/basic"],
"packages": ["packages/**"],
"node": "16"
}
1 change: 1 addition & 0 deletions .prettierignore
@@ -0,0 +1 @@
/packages/svelte-query/.svelte-kit
2 changes: 1 addition & 1 deletion babel.config.js
Expand Up @@ -33,7 +33,7 @@ module.exports = {
].filter(Boolean),
overrides: [
{
exclude: ['./packages/solid-query/**', './packages/vue-query/**'],
exclude: ['./packages/solid-query/**', './packages/svelte-query/**', './packages/vue-query/**'],
presets: ['@babel/react'],
},
{
Expand Down
2 changes: 1 addition & 1 deletion docs/config.json
Expand Up @@ -688,7 +688,7 @@
"label": "Getting Started",
"children": [
{
"label": "Coming Soon",
"label": "Overview",
"to": "svelte/overview"
}
]
Expand Down
67 changes: 64 additions & 3 deletions docs/svelte/overview.md
@@ -1,8 +1,69 @@
---
id: overview
title: Svelte Query (Coming Soon)
title: Svelte Query
---

> 鈿狅笍 This module has not yet been developed. It requires an adapter similar to `react-query` to work. We estimate the amount of code to do this is low-to-moderate, but does require familiarity with the Svelte framework. If you would like to contribute this adapter, please open a PR!
The `@tanstack/svelte-query` package offers a 1st-class API for using TanStack Query via Svelte.

The `@tanstack/svelte-query` package offers a 1st-class API for using TanStack Query via Svelte. However, all of the primitives you receive from this API are core APIs that are shared across all of the TanStack Adapters including the Query Client, query results, query subscriptions, etc.
## Example

Include the QueryClientProvider near the root of your project:

```svelte
<script lang="ts">
import { QueryClientProvider, QueryClient } from '@tanstack/svelte-query'
import Simple from './lib/Example.svelte'
const queryClient = new QueryClient()
</script>
<QueryClientProvider client={queryClient}>
<Simple />
</QueryClientProvider>
```

Then call any function (e.g. createQuery) from any component:

```svelte
<script lang="ts">
import { createQuery } from '@tanstack/svelte-query'
const query = createQuery({
queryKey: ['todos'],
queryFn: () => fetchTodos(),
})
</script>
<div>
{#if $query.isLoading}
<p>Loading...</p>
{:else if $query.isError}
<p>Error: {$query.error.message}</p>
{:else if $query.isSuccess}
{#each $query.data as todo}
<p>{todo.title}</p>
{/each}
{/if}
</div>
```

## Available Functions

Svelte Query offers useful functions and components that will make managing server state in Svelte apps easier.

- `createQuery`
- `createQueries`
- `createInfiniteQuery`
- `createMutation`
- `useQueryClient`
- `useIsFetching`
- `useIsMutating`
- `useHydrate`
- `<QueryClientProvider>`
- `<Hydrate>`

## Important Differences between Svelte Query & React Query

Svelte Query offers an API similar to React Query, but there are some key differences to be mindful of.

- Many of the functions in Svelte Query return a Svelte store. To access values on these stores reactively, you need to prefix the store with a `$`. You can learn more about Svelte stores [here](https://svelte.dev/tutorial/writable-stores).
9 changes: 9 additions & 0 deletions examples/svelte/auto-refetching/.gitignore
@@ -0,0 +1,9 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
!lib/
38 changes: 38 additions & 0 deletions examples/svelte/auto-refetching/README.md
@@ -0,0 +1,38 @@
# create-svelte

Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).

## Creating a project

If you're seeing this, you've probably already done this step. Congrats!

```bash
# create a new project in the current directory
npm create svelte@latest

# create a new project in my-app
npm create svelte@latest my-app
```

## Developing

Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:

```bash
npm run dev

# or start the server and open the app in a new browser tab
npm run dev -- --open
```

## Building

To create a production version of your app:

```bash
npm run build
```

You can preview the production build with `npm run preview`.

> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
25 changes: 25 additions & 0 deletions examples/svelte/auto-refetching/package.json
@@ -0,0 +1,25 @@
{
"name": "@tanstack/query-example-svelte-auto-refetching",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json"
},
"dependencies": {
"@tanstack/svelte-query": "^4.12.0"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^1.0.0",
"@sveltejs/kit": "^1.0.0",
"svelte": "^3.54.0",
"svelte-check": "^2.9.2",
"svelte-preprocess": "^4.10.7",
"tslib": "^2.4.1",
"typescript": "^4.7.4",
"vite": "^4.0.0"
},
"type": "module"
}
81 changes: 81 additions & 0 deletions examples/svelte/auto-refetching/src/app.css
@@ -0,0 +1,81 @@
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;

color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;

font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}

a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}

body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}

h1 {
font-size: 3.2em;
line-height: 1.1;
}

.card {
padding: 2em;
}

main {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}

button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}

@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}
9 changes: 9 additions & 0 deletions examples/svelte/auto-refetching/src/app.d.ts
@@ -0,0 +1,9 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
// and what to do when importing types
declare namespace App {
// interface Locals {}
// interface PageData {}
// interface Error {}
// interface Platform {}
}
12 changes: 12 additions & 0 deletions examples/svelte/auto-refetching/src/app.html
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" type="image/svg+xml" href="%sveltekit.assets%/emblem-light.svg" />
<meta name="viewport" content="width=device-width" />
%sveltekit.head%
</head>
<body>
<div>%sveltekit.body%</div>
</body>
</html>
12 changes: 12 additions & 0 deletions examples/svelte/auto-refetching/src/routes/+layout.svelte
@@ -0,0 +1,12 @@
<script lang="ts">
import '../app.css'
import { QueryClientProvider, QueryClient } from '@tanstack/svelte-query'
const queryClient = new QueryClient()
</script>

<QueryClientProvider client={queryClient}>
<main>
<slot />
</main>
</QueryClientProvider>
101 changes: 101 additions & 0 deletions examples/svelte/auto-refetching/src/routes/+page.svelte
@@ -0,0 +1,101 @@
<script lang="ts">
import {
useQueryClient,
createQuery,
createMutation,
} from '@tanstack/svelte-query'
let intervalMs = 1000
let value: string
const client = useQueryClient()
const endpoint = 'http://localhost:5173/api/data'
const todos = createQuery<{ items: string[] }, Error>({
queryKey: ['refetch'],
queryFn: async () => await fetch(endpoint).then((r) => r.json()),
// Refetch the data every second
refetchInterval: intervalMs,
})
const addMutation = createMutation(
(value: string) => fetch(`${endpoint}?add=${value}`).then((r) => r.json()),
{ onSuccess: () => client.invalidateQueries(['refetch']) },
)
const clearMutation = createMutation(
() => fetch(`${endpoint}?clear=1`).then((r) => r.json()),
{
onSuccess: () => client.invalidateQueries(['refetch']),
},
)
</script>

<h1>Auto Refetch with stale-time set to 1s</h1>

<p>
This example is best experienced on your own machine, where you can open
multiple tabs to the same localhost server and see your changes propagate
between the two.
</p>

<label>
Query Interval speed (ms):{' '}
<div class="flex">
<input bind:value={intervalMs} type="number" step="100" />{' '}

<span
style="display:inline-block;
margin-left:.5rem;
width:.75rem;
height:.75rem;
background: {$todos.isFetching ? 'green' : 'transparent'};
transition:: {!$todos.isFetching ? 'all .3s ease' : 'none'};
border-radius: 100%;
transform: 'scale(2)"
/>
</div>
</label>
<h2>Todo List</h2>
<form
on:submit={(e) => {
e.preventDefault()
e.stopPropagation()
$addMutation.mutate(value, {
onSuccess: () => (value = ''),
})
}}
>
<input placeholder="enter something" bind:value />
</form>

{#if $todos.isLoading}
Loading...
{/if}
{#if $todos.error}
An error has occurred:
{$todos.error.message}
{/if}
{#if $todos.isSuccess}
<ul>
{#each $todos.data.items as item}
<li>{item}</li>
{/each}
</ul>
<div>
<button on:click={() => $clearMutation.mutate(undefined)}>
Clear All
</button>
</div>
{/if}
{#if $todos.isFetching}
<div style="color:darkgreen; font-weight:700">
'Background Updating...' : ' '
</div>
{/if}

<style>
li {
text-align: left;
}
</style>

0 comments on commit b324a9b

Please sign in to comment.