Skip to content

Commit eb7ae9e

Browse files
authored
Merge pull request #98 from codediodeio/svelte4-upgrade
Svelte 4 Upgrades
2 parents 388f1ef + 961dc9f commit eb7ae9e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+16858
-3442
lines changed

.firebaserc

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"projects": {
3+
"default": "sveltefire-testing"
4+
}
5+
}

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ node_modules
99
vite.config.js.timestamp-*
1010
vite.config.ts.timestamp-*
1111
package
12+
/test-results
13+
*-debug.log
14+
/dist

README.md

+72-50
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Experimental! Do not use in production.
99
<FirebaseApp {auth} {firestore}>
1010
1111
<!-- 2. 👤 Get the current user -->
12-
<User let:user>
12+
<SignedIn let:user>
1313
1414
<p>Howdy, {user.uid}</p>
1515
@@ -38,21 +38,28 @@ Svelte makes it possible to dramatically simplify the way developers work with F
3838

3939
## Quick Start
4040

41-
1. Install Firebase `npm i firebase` v9+ and initialize it in a file like `lib/firebase.js`:
41+
1. Install Firebase `npm i firebase` v9+ and initialize it in a layout `+layout.svelte`:
4242

4343
```
4444
npm i sveltefire firebase
4545
```
4646

47-
```ts
48-
import { initializeApp } from 'firebase/app';
49-
import { getFirestore } from 'firebase/firestore';
50-
import { getAuth } from 'firebase/auth';
51-
52-
// Initialize Firebase
53-
const app = initializeApp(/* your firebase config */);
54-
export const db = getFirestore(app);
55-
export const auth = getAuth(app);
47+
```svelte
48+
<script lang="ts">
49+
import { FirebaseApp } from 'sveltefire';
50+
import { initializeApp } from 'firebase/app';
51+
import { getFirestore } from 'firebase/firestore';
52+
import { getAuth } from 'firebase/auth';
53+
54+
// Initialize Firebase
55+
const app = initializeApp(/* your firebase config */);
56+
const db = getFirestore(app);
57+
const auth = getAuth(app);
58+
</script>
59+
60+
<FirebaseApp {auth} {firestore}>
61+
<slot />
62+
</FirebaseApp>
5663
```
5764

5865
2. Get the Current user
@@ -61,7 +68,7 @@ export const auth = getAuth(app);
6168
<script>
6269
import { auth } from '$lib/firebase';
6370
import { userStore } from 'sveltefire';
64-
const user = userStore(auth);
71+
const user = userStore();
6572
</script>
6673
6774
Hello {$user?.uid}
@@ -76,7 +83,7 @@ Use the `$` as much as you want - it will only result in one Firebase read reque
7683
import { firestore } from '$lib/firebase';
7784
import { docStore } from 'sveltefire';
7885
79-
const post = docStore(firestore, 'posts/test');
86+
const post = docStore('posts/test');
8087
</script>
8188
8289
{$post?.content}
@@ -98,7 +105,7 @@ Listen to the current user. Render UI conditionally based on the auth state:
98105
<script>
99106
import { userStore } from 'sveltefire';
100107
101-
const user = userStore(auth);
108+
const user = userStore();
102109
</script>
103110
104111
{#if $user}
@@ -116,16 +123,16 @@ Subscribe to realtime data. The store will unsubscribe automatically to avoid un
116123
<script>
117124
import { docStore, collectionStore } from 'sveltefire';
118125
119-
const post = docStore(firestore, 'posts/test');
126+
const post = docStore('posts/test');
120127
121128
// OR
122129
123-
const posts = collectionStore(firestore, 'posts');
130+
const posts = collectionStore('posts');
124131
</script>
125132
126133
{$post?.content}
127134
128-
{#each $posts as p}
135+
{#each $posts as post}
129136
130137
{/each}
131138
```
@@ -138,18 +145,36 @@ interface Post {
138145
title: string;
139146
content: string;
140147
}
141-
const post = docStore<Post>(firestore, 'posts/test');
142-
const posts = collectionStore<Post>(firestore, 'posts'); // returns
148+
const post = docStore<Post>('posts/test');
149+
const posts = collectionStore<Post>('posts');
150+
```
151+
152+
## SSR
153+
154+
SvelteFire is a client-side library, but allows you to hydrate server data into a realtime stream.
155+
156+
First, fetch data from a load function like so:
157+
158+
```ts
159+
import { doc, getDoc } from 'firebase/firestore';
160+
161+
export const load = (async () => {
162+
const ref = doc(firestore, 'posts', 'first-post');
163+
const snapshot = await getDoc(ref);
164+
return {
165+
post: snapshot.data();
166+
};
167+
});
143168
```
144169

145-
Hydrate server-fetched data from SvelteKit into a realtime feed:
170+
Second, pass the server data as the `startWith` value to a store. This will bypass the loading state and ensure the data is rendered in the server HTML, then realtime listeners will be attached afterwards.
146171

147172
```ts
148173
// Data fetched via server
149174
export let data: PageData;
150175

151176
// Just give the store a startWith value
152-
const store = docStore(db, 'posts/test', data.thing);
177+
const post = docStore('posts/test', data.post);
153178
```
154179

155180
## Realtime Components
@@ -158,7 +183,8 @@ In addition to stores, SvelteFire provides a set of components that can build co
158183

159184
### FirebaseApp
160185

161-
Technically optional, this component puts Firebase into Svelte context. This avoids the need to pass `auth` and `firestore` down to every component. All other components should be nested below it.
186+
The `FirebaseApp` component puts the FirebaseSDK into Svelte context. This avoids the need to pass `auth` and `firestore` down to every component/store. It is typically placed in root layout.
187+
162188
```svelte
163189
<script>
164190
// Initialize Firebase...
@@ -174,18 +200,27 @@ Technically optional, this component puts Firebase into Svelte context. This avo
174200
</FirebaseApp>
175201
```
176202

177-
Note: Components outside a FirebaseApp will need the auth/firestore prop, i.e `<User auth={auth}>`
203+
You can easily access the Firebase SDK in any component via context. This is useful when using the Firebase SDK directly, which requires the SDK as an argument.
204+
205+
```svelte
206+
<script>
207+
import { getFirebaseContext } from "sveltefire";
208+
const { auth, firestore } = getFirebaseContext();
209+
</script>
210+
```
178211

179212
### User
180213

181214
Get the current user.
182215

183216
```svelte
184-
<User let:user>
217+
<SignedIn let:user>
185218
Hello {user.uid}
219+
</SignedIn>
186220
187-
<div slot="signedOut">You are signed out</div>
188-
</User>
221+
<SignedOut>
222+
You need to sign in!
223+
</SignedOut>
189224
```
190225

191226
### Doc
@@ -208,7 +243,7 @@ Slot props can be renamed:
208243
</Doc>
209244
```
210245

211-
All Firestore components can also handle loading states:
246+
Firestore components can also handle loading states:
212247

213248
```svelte
214249
<Doc path="posts/test">
@@ -220,7 +255,7 @@ All Firestore components can also handle loading states:
220255
Pass a `startWith` value to bypass the loading state. This is useful in SvelteKit when you need to hydrate server data into a realtime stream:
221256

222257
```svelte
223-
<Doc ref="posts/test" startWith={dataFromServer} />
258+
<Doc ref="posts/test" startWith={dataFromServer}>
224259
```
225260

226261

@@ -243,34 +278,21 @@ Collections can also take a Firestore Query instead of a path:
243278

244279
```svelte
245280
<script>
246-
const testQuery = query(collection(firestore, 'posts'), where('test', '==', 'test'));
281+
const myQuery = query(collection(firestore, 'posts'), where('test', '==', 'test'));
247282
</script>
248283
249-
<Collection ref={testQuery} let:data>
284+
<Collection ref={myQuery} let:data>
250285
</Collection>
251286
```
252287

253-
For complex queries that required dynamic data, it can be useful to build the query reactively.
254-
255-
```svelte
256-
<script>
257-
$: buildQuery = (uid:string) => {
258-
return query(collection(firestore, 'posts'), where('uid', '==', uid));
259-
}
260-
</script>
261-
262-
<User let:user>
263-
<Collection ref={buildQuery(user.uid)} />
264-
</User>
265-
```
266288
### Using Components Together
267289

268290
These components can be combined to build complex realtime apps. It's especially powerful when fetching data that requires the current user's UID or a related document's path.
269291

270292

271293
```svelte
272294
<FirebaseApp {auth} {firestore}>
273-
<User let:user>
295+
<SignedIn let:user>
274296
<p>UID: {user.uid}</p>
275297
276298
@@ -293,14 +315,14 @@ These components can be combined to build complex realtime apps. It's especially
293315
</Doc>
294316
295317
<div slot="signedOut">Signed out</div>
296-
</User>
318+
</SignedIn>
297319
</FirebaseApp>
298320
```
299321

300322

301-
## Notes
323+
## Roadmap
302324

303-
- This library should only run the the client, it is not for server-side data fetching.
304-
- Requires Firebase v9 or greater.
305-
- I've have not been able to get TS generics to work right in the components yet, so no intellisense on the `data` slot prop.
306-
- How should we bundle it properly?
325+
- Add support for Firebase Storage
326+
- Add support for Firebase RTDB
327+
- Add support for Firebase Analytics in SvelteKit
328+
- Find a way to make TS generics with with Doc/Collection components

dist/components/Collection.svelte

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>import { collectionStore } from "../stores/firestore.js";
2+
export let ref;
3+
export let startWith = void 0;
4+
let store = collectionStore(ref, startWith);
5+
</script>
6+
7+
{#if $store !== undefined}
8+
<slot data={$store} ref={store.ref} count={$store?.length ?? 0} />
9+
{:else}
10+
<slot name="loading" />
11+
{/if}
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { SvelteComponent } from "svelte";
2+
import type { CollectionReference, Query } from 'firebase/firestore';
3+
declare const __propDef: {
4+
props: {
5+
ref: string | CollectionReference | Query;
6+
startWith?: any;
7+
};
8+
events: {
9+
[evt: string]: CustomEvent<any>;
10+
};
11+
slots: {
12+
default: {
13+
data: any[];
14+
ref: CollectionReference | Query | null;
15+
count: number;
16+
};
17+
loading: {};
18+
};
19+
};
20+
export type CollectionProps = typeof __propDef.props;
21+
export type CollectionEvents = typeof __propDef.events;
22+
export type CollectionSlots = typeof __propDef.slots;
23+
export default class Collection extends SvelteComponent<CollectionProps, CollectionEvents, CollectionSlots> {
24+
}
25+
export {};

dist/components/Doc.svelte

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>import { docStore } from "../stores/firestore.js";
2+
export let ref;
3+
export let startWith = void 0;
4+
let store = docStore(ref, startWith);
5+
</script>
6+
7+
{#if $store !== undefined}
8+
<slot data={$store} ref={store.ref} />
9+
{:else}
10+
<slot name="loading" />
11+
{/if}

dist/components/Doc.svelte.d.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { SvelteComponent } from "svelte";
2+
import type { DocumentReference } from 'firebase/firestore';
3+
declare const __propDef: {
4+
props: {
5+
ref: string | DocumentReference;
6+
startWith?: any;
7+
};
8+
events: {
9+
[evt: string]: CustomEvent<any>;
10+
};
11+
slots: {
12+
default: {
13+
data: any;
14+
ref: DocumentReference | null;
15+
};
16+
loading: {};
17+
};
18+
};
19+
export type DocProps = typeof __propDef.props;
20+
export type DocEvents = typeof __propDef.events;
21+
export type DocSlots = typeof __propDef.slots;
22+
export default class Doc extends SvelteComponent<DocProps, DocEvents, DocSlots> {
23+
}
24+
export {};

dist/components/FirebaseApp.svelte

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<script>import { setFirebaseContext } from "../stores/sdk.js";
2+
export let firestore;
3+
export let auth;
4+
setFirebaseContext({ firestore, auth });
5+
</script>
6+
7+
8+
<slot />
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { SvelteComponent } from "svelte";
2+
import type { Auth } from 'firebase/auth';
3+
import type { Firestore } from 'firebase/firestore';
4+
declare const __propDef: {
5+
props: {
6+
firestore: Firestore;
7+
auth: Auth;
8+
};
9+
events: {
10+
[evt: string]: CustomEvent<any>;
11+
};
12+
slots: {
13+
default: {};
14+
};
15+
};
16+
export type FirebaseAppProps = typeof __propDef.props;
17+
export type FirebaseAppEvents = typeof __propDef.events;
18+
export type FirebaseAppSlots = typeof __propDef.slots;
19+
export default class FirebaseApp extends SvelteComponent<FirebaseAppProps, FirebaseAppEvents, FirebaseAppSlots> {
20+
}
21+
export {};

dist/components/SignedIn.svelte

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script>import { userStore } from "../stores/auth.js";
2+
import { getFirebaseContext } from "../stores/sdk.js";
3+
import { signOut } from "firebase/auth";
4+
const auth = getFirebaseContext().auth;
5+
const user = userStore();
6+
</script>
7+
8+
{#if $user}
9+
<slot user={$user} {auth} signOut={() => signOut(auth)} />
10+
{/if}

0 commit comments

Comments
 (0)