From 90e37c114b579426121958ab792a0fb141cbfeb9 Mon Sep 17 00:00:00 2001 From: "Jan T. Sott" Date: Fri, 6 Dec 2024 23:05:02 +0100 Subject: [PATCH] wip: split into multiple exports --- README.md | 10 +++--- package.json | 12 ++++--- src/{index.ts => iframe.ts} | 9 ++--- src/window.ts | 66 +++++++++++++++++++++++++++++++++++++ tsup.config.ts | 5 ++- 5 files changed, 87 insertions(+), 15 deletions(-) rename src/{index.ts => iframe.ts} (90%) create mode 100644 src/window.ts diff --git a/README.md b/README.md index 875d214..7a933f3 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,9 @@ ### Import ```ts -import { createStore } from 'svelte-crossorigin-store'; +import { createWritableStore } from 'svelte-crossorigin-store/iframe'; -const store = createStore('Hello, world'); +const store = createWritableStore('Hello, world'); const unsubscribe = store.subscribe(value => console.log('State updated:', value)); ``` @@ -32,9 +32,9 @@ Or, in your Svelte component: ```svelte

Current State: {$store}

@@ -44,7 +44,7 @@ Or, in your Svelte component: ### API -#### `createStore` +#### `createWritableStore` ```ts createStore(initialValue?: T, { diff --git a/package.json b/package.json index 7586057..c8291f7 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,14 @@ "version": "0.1.1", "description": "Share your Svelte store across origins, including iFrames", "type": "module", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", "exports": { - ".": { - "import": "./dist/index.js", - "types": "./dist/index.d.ts" + "./iframe": { + "import": "./dist/iframe.js", + "types": "./dist/iframe.d.ts" + }, + "./window": { + "import": "./dist/window.js", + "types": "./dist/window.d.ts" } }, "files": [ diff --git a/src/index.ts b/src/iframe.ts similarity index 90% rename from src/index.ts rename to src/iframe.ts index 94771ce..0a19892 100644 --- a/src/index.ts +++ b/src/iframe.ts @@ -1,3 +1,4 @@ + import { writable, type Invalidator, type Subscriber, type Writable } from 'svelte/store'; type Options = { @@ -22,7 +23,7 @@ type Options = { * }); *``` */ -export function createStore(initialValue: T, { +export function createWritableStore(initialValue: T, { allowedOrigins = ['*'], id = 'svelte-crossorigin-store:message', iframeSelector = 'iframe', @@ -47,14 +48,13 @@ export function createStore(initialValue: T, { }; }; - const _postMessageToManyOrigins = (target: HTMLIFrameElement['contentWindow'] | Window, value: any) => { + const _postMessageToManyOrigins = (target: HTMLIFrameElement['contentWindow'], value: any) => { allowedOrigins.forEach(origin => { target?.postMessage({ id, value }, origin); }); } store.subscribe(value => { - _postMessageToManyOrigins(window, value); if (window.self === window.top) { const iframes = document.querySelectorAll(iframeSelector) as NodeListOf; @@ -63,7 +63,8 @@ export function createStore(initialValue: T, { _postMessageToManyOrigins(iframe.contentWindow, value) }); } else { - _postMessageToManyOrigins(window.parent, value) + _postMessageToManyOrigins(window.parent, value); + return; } if (typeof onChange === 'function') { diff --git a/src/window.ts b/src/window.ts new file mode 100644 index 0000000..063e787 --- /dev/null +++ b/src/window.ts @@ -0,0 +1,66 @@ +import { writable, type Invalidator, type Subscriber, type Writable } from 'svelte/store'; + +type Options = { + allowedOrigins?: string[]; + id?: string; + onChange?: (value: any) => void; +}; + +/** + * Creates a writable Svelte store. + * @param initialValue + * @param options + * @returns + * @example + * ```ts + * createStore({ + * allowedOrigins = ['*'], + * id = 'svelte-crossorigin-store:message', + * onChange = undefined, + * }); + *``` + */ +export function createWritableStore(initialValue: T, { + allowedOrigins = ['*'], + id = 'svelte-crossorigin-store:message', + onChange = undefined, +}: Options = {}): Writable { + const store = writable(initialValue); + const { subscribe, set, update } = store; + + const onMessage = (event: MessageEvent) => { + if ((allowedOrigins.includes(event.origin) || allowedOrigins.includes('*')) && event.data.id === id) { + set(event.data.value); + } + }; + + const _sharedSubscribe = (run: Subscriber, invalidate: Invalidator = () => { }) => { + subscribe(run, invalidate); + + window.addEventListener('message', onMessage); + + return () => { + window.removeEventListener('message', onMessage); + }; + }; + + const _postMessageToManyOrigins = (target: Window, value: any) => { + allowedOrigins.forEach(origin => { + target?.postMessage({ id, value }, origin); + }); + } + + store.subscribe(value => { + _postMessageToManyOrigins(window, value); + + if (typeof onChange === 'function') { + onChange(value); + } + }); + + return { + subscribe: _sharedSubscribe, + set, + update, + }; +} diff --git a/tsup.config.ts b/tsup.config.ts index ba22a2f..f8d16bc 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -5,7 +5,10 @@ export default defineConfig((options) => { target: 'esnext', clean: true, dts: !options.watch, - entry: ['src/index.ts'], + entry: [ + 'src/iframe.ts', + 'src/window.ts', + ], format: 'esm', minify: !options.watch, treeshake: 'recommended'