Skip to content

Commit

Permalink
feat(@lexical/devtools): Correct handling of the background script st…
Browse files Browse the repository at this point in the history
…op/start within store
  • Loading branch information
StyleT committed Mar 21, 2024
1 parent dbd547b commit bc44908
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 1 deletion.
3 changes: 2 additions & 1 deletion packages/lexical-devtools/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
*
*/

import {wrapStore} from 'webext-zustand';
import {create} from 'zustand';

import {wrapStore} from './webext-zustand';

interface ExtensionState {
counter: number;
increase: (by: number) => void;
Expand Down
157 changes: 157 additions & 0 deletions packages/lexical-devtools/webext-zustand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import type {StoreApi} from 'zustand';

import {
Store,
wrapStore as reduxWrapStore,
} from '@eduardoac-skimlinks/webext-redux';

/* eslint-disable no-restricted-globals */

export const wrapStore = async <T>(store: StoreApi<T>) => {
const portName = PORTNAME_PREFIX + getStoreId(store);
const serializer = (payload: unknown) => JSON.stringify(payload);
const deserializer = (payload: string) => JSON.parse(payload);

if (isBackground()) {
handleBackground(store, {
deserializer,
portName,
serializer,
});

return;
} else {
return handlePages(store, {
deserializer,
portName,
serializer,
});
}
};

const handleBackground = <T>(
store: StoreApi<T>,
configuration?: BackgroundConfiguration,
) => {
reduxWrapStore(
{
//@ts-ignore
dispatch(data: {_sender: chrome.runtime.MessageSender; state: T}) {
store.setState(data.state);

return {payload: data.state};
},

getState: store.getState,

subscribe: store.subscribe,
},
configuration,
);
};

const handlePages = async <T>(
store: StoreApi<T>,
configuration?: PagesConfiguration,
) => {
const proxyStore = new Store(configuration);
// wait to be ready
await proxyStore.ready();
// initial state
store.setState(proxyStore.getState());

const callback = (state: T, oldState: T) => {
(proxyStore.dispatch({state}) as unknown as Promise<T>)
.then((syncedState) => {
if (syncedState) {
// success
} else {
// error (edge case)
// prevent infinite loop
unsubscribe();
// revert
store.setState(oldState);
// resub
unsubscribe = store.subscribe(callback);
}
})
.catch((err) => {
if (
err instanceof Error &&
err.message === 'Extension context invalidated.'
) {
console.warn(
'Reloading page as we lose connection to background script...',
);
location.reload();
return;
}
console.error('Error while during store dispatch', err);
});
};

let unsubscribe = store.subscribe(callback);

proxyStore.subscribe(() => {
// prevent retrigger
unsubscribe();
// update
store.setState(proxyStore.getState());
// resub
unsubscribe = store.subscribe(callback);
});
};

const isBackground = () => {
const isCurrentPathname = (path?: string | null) =>
path
? new URL(path, location.origin).pathname === location.pathname
: false;

const manifest = browser.runtime.getManifest();

return (
!self.window ||
(browser.extension.getBackgroundPage &&
typeof window !== 'undefined' &&
browser.extension.getBackgroundPage() === window) ||
(manifest &&
// @ts-expect-error
(isCurrentPathname(manifest.background_page) ||
(manifest.background &&
'page' in manifest.background &&
isCurrentPathname(manifest.background.page)) ||
Boolean(
manifest.background &&
'scripts' in manifest.background &&
manifest.background.scripts &&
isCurrentPathname('/_generated_background_page.html'),
)))
);
};

type BackgroundConfiguration = Parameters<typeof reduxWrapStore>[1];
type PagesConfiguration = ConstructorParameters<typeof Store>[0];

const getStoreId = (() => {
let id = 0;
const map = new WeakMap();

return <T>(store: StoreApi<T>): number => {
if (!map.has(store)) {
map.set(store, ++id);
}

return map.get(store);
};
})();

const PORTNAME_PREFIX = `webext-zustand-`;

0 comments on commit bc44908

Please sign in to comment.