{"id":19450956,"url":"https://github.com/rixo/sveltex","last_synced_at":"2025-04-25T04:30:21.604Z","repository":{"id":42988191,"uuid":"207185673","full_name":"rixo/sveltex","owner":"rixo","description":null,"archived":false,"fork":false,"pushed_at":"2023-01-07T19:44:29.000Z","size":406,"stargazers_count":5,"open_issues_count":7,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-03T15:44:48.183Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rixo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-09-08T23:17:14.000Z","updated_at":"2022-12-09T14:20:58.000Z","dependencies_parsed_at":"2023-02-07T23:15:42.234Z","dependency_job_id":null,"html_url":"https://github.com/rixo/sveltex","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rixo%2Fsveltex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rixo%2Fsveltex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rixo%2Fsveltex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rixo%2Fsveltex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rixo","download_url":"https://codeload.github.com/rixo/sveltex/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250754495,"owners_count":21481822,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-10T16:39:42.029Z","updated_at":"2025-04-25T04:30:20.892Z","avatar_url":"https://github.com/rixo.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sveltex\n\n\u003e Minimalistic DI, \u0026 async friendly stores for Svelte\n\n## FAQ\n\n### But stores are already async in Svelte?\n\nOf course, they are. What Sveltex does is allowing `async` syntax on the lifecycle function, enabling a leaner syntax with some use cases.\n\n```js\nimport { readable } from 'sveltex'\n\nconst foo = readable(\n  null,\n  // this wouldn't work in Svelte's stores (it crashes when stopping the store)\n  async set =\u003e {\n    await ...\n    return () =\u003e { ... }\n  }\n)\n```\n\n(Sveltex stores are also a little more opinionated on error management.)\n\n### What DI?\n\n**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.\n\nThe 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).\n\nThe 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](https://github.com/rixo/svench)!).\n\n### How DI?\n\n- Services are resolved from Svelte's context\n\n- The service `import` points to the default service implementation (which is great during dev to \"jump to definition\"!)\n\n- Nodes can be nested, and services can be overridden at node level\n\n  - All dependent services of a service that is overridden are recreated in this node\n\n## DI\n\n### Provider\n\n`services.js`\n\n```js\nimport { readable, derived } from 'svelte/store'\nimport { service } from 'sveltex'\n\nexport const foo = service(() =\u003e readable('foo'))\n\nexport const bar = service(() =\u003e readable('bar'))\n\nexport const foobar = service(\n  () =\u003e derived([foo, bar], ([foo$, bar$] =\u003e foo$ + bar$))\n)\n```\n\n### Container\n\n`App.svelte`\n\n```svelte\n\u003cscript\u003e\n  import { sveltex } from 'sveltex'\n  import Child from './Child.svelte'\n\n  sveltex() // internally calls setContext\n\u003c/script\u003e\n\n\u003cChild /\u003e\n```\n\n#### Override services\n\n`Child.svelte`\n\n```svelte\n\u003cscript\u003e\n  import { sveltex } from 'sveltex'\n  import { foo, bar } from './services.js'\n\n  sveltex([\n    [foo, () =\u003e readable('inner foo')],\n    [bar, () =\u003e readable('inner bar')],\n  ])\n\u003c/script\u003e\n```\n\n### Consumer\n\n`Whatever.svelte`\n\n```svelte\n\u003cscript\u003e\n  import { foobar } from './services.js'\n\n  $: value = $foobar // internally calls getContext\n\u003c/script\u003e\n\n\u003ch1\u003eHello, {$foobar}\u003c/h1\u003e\n```\n\n## Async stores\n\n```js\nimport { writable, readable, derived } from 'sveltex/store'\n```\n\nSveltex lets you define your store in a more concise \u0026 expressive way with async/generator lifecycle functions.\n\nThe lifecycle function is the function that receives `set` and returns a stop function in a `writable`, `readable` or `derived` store.\n\n```js\nimport { readable } from 'svelte/store'\n\nconst lifecycleFunction = set =\u003e {\n  ...\n  return () =\u003e { ... }\n}\n\nconst myStore = readable(lifecycleFunction)\n```\n\n### Vanilla\n\nIn 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).\n\nThis can lead to clumsy code sometimes.\n\n```js\nconst db = readable(null, set =\u003e {\n  let stopped = false\n  let stop = noop\n\n  import('./db.js')\n    .then(({ default: Db }) =\u003e {\n      if (stopped) return\n      const _db = new Db()\n      stop = () =\u003e _db.dispose()\n      set(_db)\n    })\n    .catch(err =\u003e {\n      if (stopped) return\n      if (stop) {\n        stop()\n        stopped = true\n      }\n      set(err)\n    })\n\n  return () =\u003e {\n    stopped = true\n    stop()\n  }\n}\n```\n\n### Async\n\nWith 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...\n\n```js\nconst db = sveltex.readable(null, async set =\u003e {\n  try {\n    const { default: Db } = await import('./db.js')\n    const _db = new Db()\n    set(_db) // ignored if has been stopped while waiting\n    // stop function is called immediately if stopped while waiting\n    return () =\u003e _db.dispose()\n  } catch (err) {\n    set(err)\n  }\n})\n```\n\n#### Auto catch\n\nActually, Sveltex stores catch errors and set the value of the store to the error, so the previous example can be shortened like this:\n\n```js\nconst db = sveltex.readable(null, async set =\u003e {\n  const { default: Db } = await import('./db.js')\n  const _db = new Db()\n  set(_db)\n  return () =\u003e _db.dispose()\n})\n```\n\nIf you don't want this behaviour, just catch your errors yourself.\n\n## License\n\nISC\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frixo%2Fsveltex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frixo%2Fsveltex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frixo%2Fsveltex/lists"}