npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

sveltex

v0.1.0-3

Published

Minimalistic DI & async friendly stores for Svelte

Downloads

56

Readme

Sveltex

Minimalistic DI, & async friendly stores for Svelte

FAQ

But stores are already async in Svelte?

Of course, they are. What Sveltex does is allowing async syntax on the lifecycle function, enabling a leaner syntax with some use cases.

import { readable } from 'sveltex'

const foo = readable(
  null,
  // this wouldn't work in Svelte's stores (it crashes when stopping the store)
  async set => {
    await ...
    return () => { ... }
  }
)

(Sveltex stores are also a little more opinionated on error management.)

What DI?

Dependency Injection (DI) introduces a level of indirection in your code between the provider of a service and its consumer. That is, instead of directly importing a dependency, you import an abstract service that provides the dependency.

The actual instance that you get is not directly referenced by the consumer, but it is rather "injected" into it by a DI container (that's why this pattern is also called IOC -- Inversion Of Control).

The advantage of doing so is that the code dependencies are not hardwired in the code. You can easily replace services in tests, or when developing components in isolation (e.g. with Svench!).

How DI?

  • Services are resolved from Svelte's context

  • The service import points to the default service implementation (which is great during dev to "jump to definition"!)

  • Nodes can be nested, and services can be overridden at node level

    • All dependent services of a service that is overridden are recreated in this node

DI

Provider

services.js

import { readable, derived } from 'svelte/store'
import { service } from 'sveltex'

export const foo = service(() => readable('foo'))

export const bar = service(() => readable('bar'))

export const foobar = service(
  () => derived([foo, bar], ([foo$, bar$] => foo$ + bar$))
)

Container

App.svelte

<script>
  import { sveltex } from 'sveltex'
  import Child from './Child.svelte'

  sveltex() // internally calls setContext
</script>

<Child />

Override services

Child.svelte

<script>
  import { sveltex } from 'sveltex'
  import { foo, bar } from './services.js'

  sveltex([
    [foo, () => readable('inner foo')],
    [bar, () => readable('inner bar')],
  ])
</script>

Consumer

Whatever.svelte

<script>
  import { foobar } from './services.js'

  $: value = $foobar // internally calls getContext
</script>

<h1>Hello, {$foobar}</h1>

Async stores

import { writable, readable, derived } from 'sveltex/store'

Sveltex lets you define your store in a more concise & expressive way with async/generator lifecycle functions.

The lifecycle function is the function that receives set and returns a stop function in a writable, readable or derived store.

import { readable } from 'svelte/store'

const lifecycleFunction = set => {
  ...
  return () => { ... }
}

const myStore = readable(lifecycleFunction)

Vanilla

In vanilla Svelte store, the lifecycle function can't be async (i.e. use async keyword, they can have async behaviour of course), because you have to synchronously return a cleanup function (and not a promise for a cleanup function).

This can lead to clumsy code sometimes.

const db = readable(null, set => {
  let stopped = false
  let stop = noop

  import('./db.js')
    .then(({ default: Db }) => {
      if (stopped) return
      const _db = new Db()
      stop = () => _db.dispose()
      set(_db)
    })
    .catch(err => {
      if (stopped) return
      if (stop) {
        stop()
        stopped = true
      }
      set(err)
    })

  return () => {
    stopped = true
    stop()
  }
}

Async

With Sveltex store, the lifecycle function can be async and the cleanup / error management is a little bit more opinionated, allowing for a much leaner syntax in some cases. Notably the cases where you use a store to define a Sveltex service...

const db = sveltex.readable(null, async set => {
  try {
    const { default: Db } = await import('./db.js')
    const _db = new Db()
    set(_db) // ignored if has been stopped while waiting
    // stop function is called immediately if stopped while waiting
    return () => _db.dispose()
  } catch (err) {
    set(err)
  }
})

Auto catch

Actually, Sveltex stores catch errors and set the value of the store to the error, so the previous example can be shortened like this:

const db = sveltex.readable(null, async set => {
  const { default: Db } = await import('./db.js')
  const _db = new Db()
  set(_db)
  return () => _db.dispose()
})

If you don't want this behaviour, just catch your errors yourself.

License

ISC