Skip to content

Commit

Permalink
unwrap function store (#207)
Browse files Browse the repository at this point in the history
* feat: add unwrapFunctionStore utility

* chore(release): v3.6.0 🎉
  • Loading branch information
kaisermann authored Nov 25, 2022
1 parent 113301e commit ebca372
Show file tree
Hide file tree
Showing 21 changed files with 144 additions and 16 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
# [3.6.0](https://github.com/kaisermann/svelte-i18n/compare/v3.4.0...v3.6.0) (2022-11-25)


### Bug Fixes

* better handling of --config CLI parameter ([124d4b7](https://github.com/kaisermann/svelte-i18n/commit/124d4b7b58adb48292829258db393931306d3fd0))
* make typescript strict and fix small issues ([7b49a74](https://github.com/kaisermann/svelte-i18n/commit/7b49a74f0fade6aaee42b4d137dc96d22ec55b2b))
* require node 16 ([bf622da](https://github.com/kaisermann/svelte-i18n/commit/bf622da160c555180e5f48e407d64d8b6d8ef6d7))
* svelte.config not being loaded properly by CLI ([309896c](https://github.com/kaisermann/svelte-i18n/commit/309896cc7cca9018562eaa7f6845b64253900f54))


### Features

* add unwrapFunctionStore utility ([fafa641](https://github.com/kaisermann/svelte-i18n/commit/fafa641c8151c4ceb21a1d9558b535f9eb56dbf9))



## [3.5.2](https://github.com/kaisermann/svelte-i18n/compare/v3.4.0...v3.5.2) (2022-11-23)


Expand Down
21 changes: 21 additions & 0 deletions docs/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,24 @@ export default {
}

```

##### `How can I use svelte-i18n with SvelteKit?`

You can find a guide on how to use `svelte-i18n` with `SvelteKit` [here](/docs/Svelte-Kit.md).

##### `Can I use the formatter functions outside of a Svelte component?`

Yes, you can! Since the exported formatters are stores, you need to subscribe to them to get the value. `svelte-i18n` provides a utility method called `unwrapFunctionStore` to help you with that:

```js
// some-file.js
import { unwrapFunctionStore, format, formatNumber } from 'svelte-i18n';

const $formatNumber = unwrapFunctionStore(formatNumber);
const $format = unwrapFunctionStore(format);

console.log(
$formatNumber(1000, 'en-US', { style: 'currency', currency: 'USD' }),
); // $1,000.00
console.log($format('Hello {name}', { name: 'John' }, 'en-US')); // Hello John
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "svelte-i18n",
"version": "3.5.2",
"version": "3.6.0",
"main": "dist/runtime.cjs.js",
"module": "dist/runtime.esm.js",
"types": "dist/runtime.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/configs.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { $locale, getCurrentLocale, getPossibleLocales } from './stores/locale';
import { hasLocaleQueue } from './includes/loaderQueue';
import { hasLocaleQueue } from './modules/loaderQueue';

import type {
ConfigureOptions,
Expand Down
10 changes: 6 additions & 4 deletions src/runtime/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { getCurrentLocale, $locale } from './stores/locale';
import { getOptions, init } from './configs';
import { flush, registerLocaleLoader } from './includes/loaderQueue';
import { flush, registerLocaleLoader } from './modules/loaderQueue';
import {
getLocaleFromHostname,
getLocaleFromPathname,
getLocaleFromNavigator,
getLocaleFromQueryString,
getLocaleFromHash,
} from './includes/localeGetters';
} from './modules/localeGetters';
import { $dictionary, $locales, addMessages } from './stores/dictionary';
import { $isLoading } from './stores/loading';
import {
Expand All @@ -22,9 +22,10 @@ import {
getNumberFormatter,
getTimeFormatter,
getMessageFormatter,
} from './includes/formatters';
} from './modules/formatters';
import { unwrapFunctionStore } from './modules/unwrapFunctionStore';

import type { MessageObject } from './types';
import type { MessageObject } from './types/index';

// defineMessages allow us to define and extract dynamic message ids
export function defineMessages(i: Record<string, MessageObject>) {
Expand Down Expand Up @@ -60,6 +61,7 @@ export {
getTimeFormatter,
getMessageFormatter,
// utils
unwrapFunctionStore,
getLocaleFromHostname,
getLocaleFromPathname,
getLocaleFromNavigator,
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
42 changes: 42 additions & 0 deletions src/runtime/modules/unwrapFunctionStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { Readable } from 'svelte/store';

type UnwrapStore<T> = T extends Readable<infer U> ? U : T;

/**
* Unwraps a function from a store and make it function calleable easily outside of a Svelte component.
*
* It works by creating a subscription to the store and getting local reference to the store value.
* Then when the returned function is called, it will execute the function by using the local reference.
*
* The returned function has a 'freeze' method that will stop listening to the store.
*
* @example
* // some-js-file.js
* import { format } from 'svelte-i18n';
*
* const $format = unwrapFunctionStore(format);
*
* console.log($format('hello', { name: 'John' }));
*
*/
export function unwrapFunctionStore<
S extends Readable<(...args: any[]) => any>,
Fn extends UnwrapStore<S>,
>(
store: S,
): Fn & {
/**
* Stops listening to the store.
*/
freeze: () => void;
} {
let localReference: Fn;

const cancel = store.subscribe((value) => (localReference = value as Fn));

const fn = (...args: Parameters<Fn>) => localReference(...args);

fn.freeze = cancel;

return fn as Fn & { freeze: () => void };
}
2 changes: 1 addition & 1 deletion src/runtime/stores/dictionary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import deepmerge from 'deepmerge';

import { getPossibleLocales } from './locale';
import { delve } from '../../shared/delve';
import { lookupCache } from '../includes/lookup';
import { lookupCache } from '../modules/lookup';

import type { LocaleDictionary, LocalesDictionary } from '../types/index';

Expand Down
4 changes: 2 additions & 2 deletions src/runtime/stores/formatters.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { derived } from 'svelte/store';

import { lookup } from '../includes/lookup';
import { lookup } from '../modules/lookup';
import {
getMessageFormatter,
getTimeFormatter,
getDateFormatter,
getNumberFormatter,
} from '../includes/formatters';
} from '../modules/formatters';
import { getOptions } from '../configs';
import { $dictionary } from './dictionary';
import { getCurrentLocale, $locale } from './locale';
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/stores/locale.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { writable } from 'svelte/store';

import { flush, hasLocaleQueue } from '../includes/loaderQueue';
import { flush, hasLocaleQueue } from '../modules/loaderQueue';
import { getOptions } from '../configs';
import { getClosestAvailableLocale } from './dictionary';
import { $isLoading } from './loading';
Expand Down
2 changes: 1 addition & 1 deletion test/runtime/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { defineMessages, waitLocale, register, init } from '../../src/runtime';
import { $locale } from '../../src/runtime/stores/locale';
import { hasLocaleQueue } from '../../src/runtime/includes/loaderQueue';
import { hasLocaleQueue } from '../../src/runtime/modules/loaderQueue';
import {
getLocaleDictionary,
$dictionary,
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
flush,
registerLocaleLoader,
resetQueues,
} from '../../../src/runtime/includes/loaderQueue';
} from '../../../src/runtime/modules/loaderQueue';
import { getMessageFromDictionary } from '../../../src/runtime/stores/dictionary';

beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { init } from '../../../src/runtime/configs';
import { lookup, lookupCache } from '../../../src/runtime/includes/lookup';
import { lookup, lookupCache } from '../../../src/runtime/modules/lookup';
import {
$dictionary,
addMessages,
Expand Down
46 changes: 46 additions & 0 deletions test/runtime/modules/unwrapFunctionStore.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { writable, derived, get } from 'svelte/store';

import { unwrapFunctionStore } from '../../../src/runtime';

import type { Readable } from 'svelte/store';

test('unwraps the function from a store', () => {
const store = writable(0);
const functionStore = derived(store, ($store) => () => $store) as Readable<
() => number
>;

const unwrapped = unwrapFunctionStore(functionStore);

expect(get(functionStore)()).toBe(0);
expect(unwrapped()).toBe(0);

store.set(1);

expect(get(functionStore)()).toBe(1);
expect(unwrapped()).toBe(1);
});

test('stops listening to store changes when .freeze is called', () => {
const store = writable(0);
const functionStore = derived(store, ($store) => () => $store) as Readable<
() => number
>;

const unwrapped = unwrapFunctionStore(functionStore);

expect(get(functionStore)()).toBe(0);
expect(unwrapped()).toBe(0);

store.set(1);

expect(get(functionStore)()).toBe(1);
expect(unwrapped()).toBe(1);

unwrapped.freeze();

store.set(2);

expect(get(functionStore)()).toBe(2);
expect(unwrapped()).toBe(1);
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
getLocaleFromNavigator,
getLocaleFromPathname,
getLocaleFromHostname,
} from '../../../src/runtime/includes/localeGetters';
} from '../../../src/runtime/modules/localeGetters';

describe('getting client locale', () => {
beforeEach(() => {
Expand Down
4 changes: 2 additions & 2 deletions test/runtime/stores/locale.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { get } from 'svelte/store';

import { lookup } from '../../../src/runtime/includes/lookup';
import { lookup } from '../../../src/runtime/modules/lookup';
import {
getPossibleLocales,
getCurrentLocale,
$locale,
} from '../../../src/runtime/stores/locale';
import { getOptions, init } from '../../../src/runtime/configs';
import { register, isLoading } from '../../../src/runtime';
import { hasLocaleQueue } from '../../../src/runtime/includes/loaderQueue';
import { hasLocaleQueue } from '../../../src/runtime/modules/loaderQueue';

beforeEach(() => {
init({ fallbackLocale: undefined as any });
Expand Down

0 comments on commit ebca372

Please sign in to comment.