diff --git a/examples/basic/src/main/index.ts b/examples/basic/src/main/index.ts index a841b980..5290aba1 100644 --- a/examples/basic/src/main/index.ts +++ b/examples/basic/src/main/index.ts @@ -1,9 +1,10 @@ import url from 'url' import { app, BrowserWindow } from 'electron' -import { stateSyncEnhancer } from 'electron-redux' +import { stateSyncEnhancer } from 'electron-redux/main' import { createStore } from 'redux' import { rootReducer } from '../store' +const TESTING = process.env.SPECTRON === 'true' // ================================================================== // electron related boiler-plate to create window with singe renderer let mainWindow: BrowserWindow | null @@ -13,7 +14,11 @@ async function createWindow() { width: 800, height: 600, webPreferences: { - nodeIntegration: true, + preload: `${__dirname}/preload.js`, + // PROD app should be running with contextIsolation: true for security reasons. Disabled only while running e2e tests + contextIsolation: !TESTING, + // ONLY TRUE FOR TESTING - SPECTRON needs node integration to be able to access the remote modules. + nodeIntegration: TESTING, }, }) await mainWindow.loadURL( diff --git a/examples/basic/src/renderer/index.ts b/examples/basic/src/renderer/index.ts index f90b5dbf..552d44bf 100644 --- a/examples/basic/src/renderer/index.ts +++ b/examples/basic/src/renderer/index.ts @@ -2,7 +2,7 @@ import { createStore } from 'redux' import { rootReducer } from '../store' -import { stateSyncEnhancer } from 'electron-redux' +import { stateSyncEnhancer } from 'electron-redux/renderer' import { decrementGlobalCounter, decrementLocalCounter, diff --git a/examples/basic/src/renderer/preload.ts b/examples/basic/src/renderer/preload.ts new file mode 100644 index 00000000..dfd3e38b --- /dev/null +++ b/examples/basic/src/renderer/preload.ts @@ -0,0 +1,2 @@ +// Include me in your preload script! +require('electron-redux/preload') diff --git a/examples/basic/webpack.renderer.js b/examples/basic/webpack.renderer.js index 53f8a22b..0e4ee33f 100644 --- a/examples/basic/webpack.renderer.js +++ b/examples/basic/webpack.renderer.js @@ -9,6 +9,7 @@ module.exports = { mode: 'production', entry: { renderer: './src/renderer/index.ts', + preload: './src/renderer/preload.ts', }, target: 'electron-renderer', plugins: [ diff --git a/main/package.json b/main/package.json new file mode 100644 index 00000000..c8b5d6c5 --- /dev/null +++ b/main/package.json @@ -0,0 +1,6 @@ +{ + "internal": true, + "main": "../lib/main.js", + "module": "../es/main.js", + "types": "../types/main.d.ts" +} diff --git a/package.json b/package.json index eee20b96..339924a1 100644 --- a/package.json +++ b/package.json @@ -10,13 +10,16 @@ ], "license": "MIT", "private": false, - "main": "lib/electron-redux.js", - "module": "es/electron-redux.js", + "main": "lib/index.js", + "module": "es/index.js", "types": "types/index.d.ts", "files": [ "lib", "es", - "types" + "types", + "main", + "renderer", + "preload" ], "scripts": { "clean": "rimraf lib es coverage types", @@ -48,7 +51,6 @@ "@babel/preset-env": "^7.11.5", "@babel/preset-typescript": "^7.10.4", "@rollup/plugin-commonjs": "^15.0.0", - "@rollup/plugin-node-resolve": "^9.0.0", "@types/jest": "^26.0.14", "@types/lodash.isplainobject": "^4.0.6", "@types/lodash.isstring": "^4.0.6", diff --git a/preload/package.json b/preload/package.json new file mode 100644 index 00000000..15ef9312 --- /dev/null +++ b/preload/package.json @@ -0,0 +1,6 @@ +{ + "internal": true, + "main": "../lib/preload.js", + "module": "../es/preload.js", + "types": "../types/preload.d.ts" +} diff --git a/renderer/package.json b/renderer/package.json new file mode 100644 index 00000000..481546ab --- /dev/null +++ b/renderer/package.json @@ -0,0 +1,6 @@ +{ + "internal": true, + "main": "../lib/renderer.js", + "module": "../es/renderer.js", + "types": "../types/renderer.d.ts" +} diff --git a/rollup.config.js b/rollup.config.js index aa5143f4..9cc15d6a 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,4 +1,3 @@ -import nodeResolve from '@rollup/plugin-node-resolve' import babel from 'rollup-plugin-babel' import commonjs from '@rollup/plugin-commonjs' import typescript from 'rollup-plugin-typescript2' @@ -7,38 +6,26 @@ import pkg from './package.json' const extensions = ['.ts'] -const basePlugins = [ - commonjs(), - nodeResolve({ - extensions, - }), - typescript({ useTsconfigDeclarationDir: true }), -] +const basePlugins = [commonjs(), typescript({ useTsconfigDeclarationDir: true })] + +const baseConfig = { + external: Object.keys(pkg.peerDependencies || {}), + plugins: [ + ...basePlugins, + babel({ + extensions, + }), + ], +} export default [ // CommonJS { - input: 'src/index.ts', - output: { file: 'lib/electron-redux.js', format: 'cjs', indent: false }, - external: Object.keys(pkg.peerDependencies || {}), - plugins: [ - ...basePlugins, - babel({ - extensions, - }), - ], - }, - - // ES - { - input: 'src/index.ts', - output: { file: 'es/electron-redux.js', format: 'es', indent: false }, - external: Object.keys(pkg.peerDependencies || {}), - plugins: [ - ...basePlugins, - babel({ - extensions, - }), + ...baseConfig, + input: ['src/index.ts', 'src/main.ts', 'src/renderer.ts', 'src/preload.ts'], + output: [ + { dir: 'lib', format: 'cjs' }, + { dir: 'es', format: 'es' }, ], }, ] diff --git a/src/composeWithStateSync.ts b/src/composeWithStateSync.ts index b512d4c3..cdc4e094 100644 --- a/src/composeWithStateSync.ts +++ b/src/composeWithStateSync.ts @@ -1,44 +1,51 @@ /* eslint-disable @typescript-eslint/ban-types */ import { StoreEnhancer } from 'redux' -import { forwardAction } from './forwardAction' +import { forwardAction, ProcessForwarder } from './utils/forwardAction' import { StateSyncOptions } from './options/StateSyncOptions' -import { stateSyncEnhancer } from './stateSyncEnhancer' +import { StateSyncEnhancer } from './utils/types' -const forwardActionEnhancer = (options?: StateSyncOptions): StoreEnhancer => (createStore) => ( - reducer, - preloadedState -) => { +const forwardActionEnhancer = ( + processForwarder: ProcessForwarder, + options?: StateSyncOptions +): StoreEnhancer => (createStore) => (reducer, preloadedState) => { const store = createStore(reducer, preloadedState) - return forwardAction(store, options) + return forwardAction(store, processForwarder, options) } -const extensionCompose = (options: StateSyncOptions) => ( - ...funcs: StoreEnhancer[] -): StoreEnhancer => { +const extensionCompose = ( + stateSyncEnhancer: StateSyncEnhancer, + processForwarder: ProcessForwarder, + options: StateSyncOptions +) => (...funcs: StoreEnhancer[]): StoreEnhancer => { return (createStore) => { return [ stateSyncEnhancer({ ...options, preventActionReplay: true }), ...funcs, - forwardActionEnhancer(options), + forwardActionEnhancer(processForwarder, options), ].reduceRight((composed, f) => f(composed), createStore) } } -export function composeWithStateSync( - options: StateSyncOptions -): (...funcs: Function[]) => StoreEnhancer -export function composeWithStateSync(...funcs: StoreEnhancer[]): StoreEnhancer -export function composeWithStateSync( - firstFuncOrOpts: StoreEnhancer | StateSyncOptions, - ...funcs: StoreEnhancer[] -): StoreEnhancer | ((...funcs: StoreEnhancer[]) => StoreEnhancer) { - if (arguments.length === 0) { - return stateSyncEnhancer() - } - if (arguments.length === 1 && typeof firstFuncOrOpts === 'object') { - return extensionCompose(firstFuncOrOpts) +export function createComposer( + stateSyncEnhancer: StateSyncEnhancer, + processForwarder: ProcessForwarder +) { + return function composeWithStateSync( + firstFuncOrOpts: StoreEnhancer | StateSyncOptions, + ...funcs: Array + ): StoreEnhancer { + if (arguments.length === 0) { + return stateSyncEnhancer({}) + } + if (arguments.length === 1 && typeof firstFuncOrOpts === 'object') { + return extensionCompose(stateSyncEnhancer, processForwarder, firstFuncOrOpts)() + } + return extensionCompose( + stateSyncEnhancer, + processForwarder, + {} + )(firstFuncOrOpts as StoreEnhancer, ...funcs) } - return extensionCompose({})(firstFuncOrOpts as StoreEnhancer, ...funcs) } diff --git a/src/fetchState/index.ts b/src/fetchState/index.ts deleted file mode 100644 index 4af0da40..00000000 --- a/src/fetchState/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import fetchInitialState from './fetchInitialState' -import fetchInitialStateAsync from './fetchInitialStateAsync' - -export { fetchInitialState, fetchInitialStateAsync } diff --git a/src/forwardAction.ts b/src/forwardAction.ts deleted file mode 100644 index 5bb73bd9..00000000 --- a/src/forwardAction.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { ipcRenderer, webContents } from 'electron' -import { Store } from 'redux' -import { IPCEvents } from './constants' -import { MainStateSyncEnhancerOptions } from './options/MainStateSyncEnhancerOptions' -import { RendererStateSyncEnhancerOptions } from './options/RendererStateSyncEnhancerOptions' -import { StateSyncOptions } from './options/StateSyncOptions' -import { isMain, isRenderer, validateAction } from './utils' - -export const processActionMain = ( - action: A, - options: MainStateSyncEnhancerOptions = {} -): void => { - if (validateAction(action, options.denyList)) { - webContents.getAllWebContents().forEach((contents) => { - // Ignore chromium devtools - if (contents.getURL().startsWith('devtools://')) return - contents.send(IPCEvents.ACTION, action) - }) - } -} - -export const processActionRenderer = ( - action: A, - options: RendererStateSyncEnhancerOptions = {} -): void => { - if (validateAction(action, options.denyList)) { - ipcRenderer.send(IPCEvents.ACTION, action) - } -} - -export const forwardAction = >( - store: S, - options?: StateSyncOptions -): S => { - return { - ...store, - dispatch: (action) => { - const value = store.dispatch(action) - - if (!options?.preventActionReplay) { - if (isMain) { - processActionMain(action, options) - } else if (isRenderer) { - processActionRenderer(action, options) - } - } - - return value - }, - } -} diff --git a/src/index.ts b/src/index.ts index 23e259f4..63366cad 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,3 @@ -import { mainStateSyncEnhancer } from './mainStateSyncEnhancer' -import { stopForwarding } from './utils' -import { rendererStateSyncEnhancer } from './rendererStateSyncEnhancer' -import { stateSyncEnhancer } from './stateSyncEnhancer' -import { composeWithStateSync } from './composeWithStateSync' +import { stopForwarding } from './utils/actions' -export { - mainStateSyncEnhancer, - rendererStateSyncEnhancer, - stopForwarding, - stateSyncEnhancer, - composeWithStateSync, -} +export { stopForwarding } diff --git a/src/mainStateSyncEnhancer.ts b/src/main.ts similarity index 63% rename from src/mainStateSyncEnhancer.ts rename to src/main.ts index 801242a3..c9705ca8 100644 --- a/src/mainStateSyncEnhancer.ts +++ b/src/main.ts @@ -1,18 +1,23 @@ import { ipcMain, webContents } from 'electron' import { Action, StoreEnhancer } from 'redux' import { IPCEvents } from './constants' -import { forwardAction } from './forwardAction' +import { forwardAction } from './utils/forwardAction' +import { forwardActionToRenderers } from './main/forwardActionToRenderers' import { MainStateSyncEnhancerOptions } from './options/MainStateSyncEnhancerOptions' -import { stopForwarding } from './utils' +import { preventDoubleInitialization, stopForwarding } from './utils' +import { StateSyncOptions } from './options/StateSyncOptions' +import { createComposer } from './composeWithStateSync' /** * Creates new instance of main process redux enhancer. * @param {MainStateSyncEnhancerOptions} options Additional enhancer options * @returns StoreEnhancer */ -export const mainStateSyncEnhancer = ( - options: MainStateSyncEnhancerOptions = {} -): StoreEnhancer => (createStore) => { +export const stateSyncEnhancer = (options: MainStateSyncEnhancerOptions = {}): StoreEnhancer => ( + createStore +) => { + preventDoubleInitialization() + return (reducer, preloadedState) => { const store = createStore(reducer, preloadedState) @@ -42,6 +47,13 @@ export const mainStateSyncEnhancer = ( }) }) - return forwardAction(store, options) + return forwardAction(store, forwardActionToRenderers, options) } } + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export const composeWithStateSync = ( + firstFuncOrOpts: StoreEnhancer | StateSyncOptions, + ...funcs: StoreEnhancer[] +): StoreEnhancer => + createComposer(stateSyncEnhancer, forwardActionToRenderers)(firstFuncOrOpts, ...funcs) diff --git a/src/main/forwardActionToRenderers.ts b/src/main/forwardActionToRenderers.ts new file mode 100644 index 00000000..e6435533 --- /dev/null +++ b/src/main/forwardActionToRenderers.ts @@ -0,0 +1,17 @@ +import { webContents } from 'electron' +import { IPCEvents } from 'src/constants' +import { MainStateSyncEnhancerOptions } from 'src/options/MainStateSyncEnhancerOptions' +import { validateAction } from 'src/utils' + +export const forwardActionToRenderers = ( + action: A, + options: MainStateSyncEnhancerOptions = {} +): void => { + if (validateAction(action, options.denyList)) { + webContents.getAllWebContents().forEach((contents) => { + // Ignore chromium devtools + if (contents.getURL().startsWith('devtools://')) return + contents.send(IPCEvents.ACTION, action) + }) + } +} diff --git a/src/preload.ts b/src/preload.ts new file mode 100644 index 00000000..beb52c1d --- /dev/null +++ b/src/preload.ts @@ -0,0 +1,71 @@ +import { contextBridge } from 'electron' +import { StoreEnhancer, Action } from 'redux' +import { createComposer } from './composeWithStateSync' +import { RendererStateSyncEnhancerOptions } from './options/RendererStateSyncEnhancerOptions' +import { StateSyncOptions } from './options/StateSyncOptions' +import { fetchInitialState } from './renderer/fetchInitialState' +import { fetchInitialStateAsync } from './renderer/fetchInitialStateAsync' +import { forwardActionToMain } from './renderer/forwardActionToMain' +import { subscribeToIPCAction } from './renderer/subscribeToIPCAction' +import { preventDoubleInitialization, stopForwarding } from './utils' +import { forwardAction } from './utils/forwardAction' +import { withStoreReplacer, replaceState } from './utils/replaceState' + +declare global { + interface Bridge { + stateSyncEnhancer: typeof stateSyncEnhancer + composeWithStateSync: typeof composeWithStateSync + } + interface Window { + __ElectronReduxBridge: Bridge + } + + const __ElectronReduxBridge: Bridge +} + +const stateSyncEnhancer = (options: RendererStateSyncEnhancerOptions = {}): StoreEnhancer => ( + createStore +) => { + preventDoubleInitialization() + + return (reducer, state) => { + const initialState = options.lazyInit ? state : fetchInitialState(options) + + const store = createStore( + options.lazyInit ? withStoreReplacer(reducer) : reducer, + initialState + ) + + if (options.lazyInit) { + fetchInitialStateAsync(options, (asyncState) => { + store.dispatch(replaceState(asyncState) as never) + }) + } + + // When receiving an action from main + subscribeToIPCAction((action: Action) => store.dispatch(stopForwarding(action))) + + return forwardAction(store, forwardActionToMain, options) + } +} + +const composeWithStateSync = ( + firstFuncOrOpts: StoreEnhancer | StateSyncOptions, + ...funcs: StoreEnhancer[] +) => createComposer(stateSyncEnhancer, forwardActionToMain)(firstFuncOrOpts, ...funcs) + +export const preload = (): void => { + const bridge = { + stateSyncEnhancer, + composeWithStateSync, + } + + try { + contextBridge.exposeInMainWorld('__ElectronReduxBridge', bridge) + } catch { + window.__ElectronReduxBridge = bridge + } +} + +// run it! +preload() diff --git a/src/renderer.ts b/src/renderer.ts new file mode 100644 index 00000000..59f6ed95 --- /dev/null +++ b/src/renderer.ts @@ -0,0 +1,36 @@ +import { StoreEnhancer } from 'redux' +import { RendererStateSyncEnhancerOptions } from './options/RendererStateSyncEnhancerOptions' +import { preventDoubleInitialization } from './utils' +import { StateSyncOptions } from './options/StateSyncOptions' + +/** + * Creates new instance of renderer process redux enhancer. + * Upon initialization, it will fetch the state from the main process & subscribe for event + * communication required to keep the actions in sync. + * @param {RendererStateSyncEnhancerOptions} options Additional settings for enhancer + * @returns StoreEnhancer + */ +export const stateSyncEnhancer = ( + options: RendererStateSyncEnhancerOptions = {} +): StoreEnhancer => { + preventDoubleInitialization() + assertElectronReduxBridgeAvailability() + return __ElectronReduxBridge.stateSyncEnhancer(options) +} + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export const composeWithStateSync = ( + firstFuncOrOpts: StoreEnhancer | StateSyncOptions, + ...funcs: StoreEnhancer[] +) => { + assertElectronReduxBridgeAvailability() + return __ElectronReduxBridge.composeWithStateSync(firstFuncOrOpts, ...funcs) +} + +const assertElectronReduxBridgeAvailability = () => { + if (typeof __ElectronReduxBridge === undefined) { + throw new Error( + 'Looks like this renderer process has not been configured properly. Did you forgot to include preload script?' + ) + } +} diff --git a/src/fetchState/fetchInitialState.ts b/src/renderer/fetchInitialState.ts similarity index 72% rename from src/fetchState/fetchInitialState.ts rename to src/renderer/fetchInitialState.ts index 88dfe693..09ba8df0 100644 --- a/src/fetchState/fetchInitialState.ts +++ b/src/renderer/fetchInitialState.ts @@ -2,9 +2,7 @@ import { ipcRenderer } from 'electron' import { IPCEvents } from '../constants' import { RendererStateSyncEnhancerOptions } from '../options/RendererStateSyncEnhancerOptions' -function fetchInitialState(options: RendererStateSyncEnhancerOptions): T { +export function fetchInitialState(options: RendererStateSyncEnhancerOptions): T { const state = ipcRenderer.sendSync(IPCEvents.INIT_STATE) return JSON.parse(state, options.deserializer) } - -export default fetchInitialState diff --git a/src/fetchState/fetchInitialStateAsync.ts b/src/renderer/fetchInitialStateAsync.ts similarity index 90% rename from src/fetchState/fetchInitialStateAsync.ts rename to src/renderer/fetchInitialStateAsync.ts index ac785479..a8c6477b 100644 --- a/src/fetchState/fetchInitialStateAsync.ts +++ b/src/renderer/fetchInitialStateAsync.ts @@ -2,7 +2,7 @@ import { ipcRenderer } from 'electron' import { IPCEvents } from '../constants' import { RendererStateSyncEnhancerOptions } from '../options/RendererStateSyncEnhancerOptions' -async function fetchInitialStateAsync( +export async function fetchInitialStateAsync( options: RendererStateSyncEnhancerOptions, callback: (state: unknown) => void ): Promise { @@ -18,5 +18,3 @@ async function fetchInitialStateAsync( ) } } - -export default fetchInitialStateAsync diff --git a/src/renderer/forwardActionToMain.ts b/src/renderer/forwardActionToMain.ts new file mode 100644 index 00000000..3d9d4241 --- /dev/null +++ b/src/renderer/forwardActionToMain.ts @@ -0,0 +1,13 @@ +import { ipcRenderer } from 'electron' +import { IPCEvents } from '../constants' +import { RendererStateSyncEnhancerOptions } from '../options/RendererStateSyncEnhancerOptions' +import { validateAction } from '../utils' + +export const forwardActionToMain = ( + action: A, + options: RendererStateSyncEnhancerOptions = {} +): void => { + if (validateAction(action, options.denyList)) { + ipcRenderer.send(IPCEvents.ACTION, action) + } +} diff --git a/src/renderer/subscribeToIPCAction.ts b/src/renderer/subscribeToIPCAction.ts new file mode 100644 index 00000000..229596aa --- /dev/null +++ b/src/renderer/subscribeToIPCAction.ts @@ -0,0 +1,9 @@ +import { ipcRenderer } from 'electron' +import { Action } from 'redux' +import { IPCEvents } from 'src/constants' + +export const subscribeToIPCAction = (callback: (action: Action) => void): void => { + ipcRenderer.on(IPCEvents.ACTION, (_, action: Action) => { + callback(action) + }) +} diff --git a/src/rendererStateSyncEnhancer.ts b/src/rendererStateSyncEnhancer.ts deleted file mode 100644 index 52f3a978..00000000 --- a/src/rendererStateSyncEnhancer.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { ipcRenderer } from 'electron' -import { Action, StoreEnhancer } from 'redux' -import { IPCEvents } from './constants' -import { forwardAction } from './forwardAction' -import { fetchInitialState, fetchInitialStateAsync } from './fetchState' -import { replaceState, withStoreReplacer } from './fetchState/replaceState' -import { RendererStateSyncEnhancerOptions } from './options/RendererStateSyncEnhancerOptions' -import { stopForwarding } from './utils' - -/** - * Creates new instance of renderer process redux enhancer. - * Upon initialization, it will fetch the state from the main process & subscribe for event - * communication required to keep the actions in sync. - * @param {RendererStateSyncEnhancerOptions} options Additional settings for enhancer - * @returns StoreEnhancer - */ -export const rendererStateSyncEnhancer = ( - options: RendererStateSyncEnhancerOptions = {} -): StoreEnhancer => (createStore) => { - return (reducer, state) => { - const initialState = options.lazyInit ? state : fetchInitialState(options) - - const store = createStore( - options.lazyInit ? withStoreReplacer(reducer) : reducer, - initialState - ) - - if (options.lazyInit) { - fetchInitialStateAsync(options, (asyncState) => { - store.dispatch(replaceState(asyncState) as never) - }) - } - - // When receiving an action from main - ipcRenderer.on(IPCEvents.ACTION, (_, action: Action) => { - store.dispatch(stopForwarding(action)) - }) - - return forwardAction(store, options) - } -} diff --git a/src/stateSyncEnhancer.ts b/src/stateSyncEnhancer.ts deleted file mode 100644 index 4c042ec0..00000000 --- a/src/stateSyncEnhancer.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { StoreEnhancer } from 'redux' -import { mainStateSyncEnhancer } from './mainStateSyncEnhancer' -import { StateSyncOptions } from './options/StateSyncOptions' -import { rendererStateSyncEnhancer } from './rendererStateSyncEnhancer' -import { isMain, isRenderer, preventDoubleInitialization } from './utils' - -export const stateSyncEnhancer = (config: StateSyncOptions = {}): StoreEnhancer => { - preventDoubleInitialization() - - if (isRenderer) { - return rendererStateSyncEnhancer(config) - } else if (isMain) { - return mainStateSyncEnhancer(config) - } - - throw new Error(`Unsupported process: process.type = ${process?.type}`) -} diff --git a/src/utils/forwardAction.ts b/src/utils/forwardAction.ts new file mode 100644 index 00000000..339bb631 --- /dev/null +++ b/src/utils/forwardAction.ts @@ -0,0 +1,23 @@ +import { Store } from 'redux' +import { StateSyncOptions } from '../options/StateSyncOptions' + +export type ProcessForwarder = (forwarderAction: any, forwarderOptions: StateSyncOptions) => void + +export const forwardAction = >( + store: S, + processForwarder: ProcessForwarder, + options: StateSyncOptions = {} +): S => { + return { + ...store, + dispatch: (action) => { + const value = store.dispatch(action) + + if (!options?.preventActionReplay) { + processForwarder(action, options) + } + + return value + }, + } +} diff --git a/src/utils/misc.ts b/src/utils/misc.ts index 2b75a2b6..8fa915b3 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -31,6 +31,3 @@ export const trimProperties = (props: T[], obj: X) => { Object.entries(obj).filter(([key]) => !props.includes(key as T)) ) as Omit } - -export const isRenderer = process.type === 'renderer' -export const isMain = process.type === 'browser' diff --git a/src/fetchState/replaceState.ts b/src/utils/replaceState.ts similarity index 90% rename from src/fetchState/replaceState.ts rename to src/utils/replaceState.ts index da10a81f..34e88fb5 100644 --- a/src/fetchState/replaceState.ts +++ b/src/utils/replaceState.ts @@ -1,6 +1,6 @@ import { AnyAction, Reducer } from 'redux' -import { FluxStandardAction } from '../utils/isFSA' -import { ActionMeta } from '../utils' +import { FluxStandardAction } from './isFSA' +import { ActionMeta } from '.' interface ReplaceStateAction extends FluxStandardAction { payload: S diff --git a/src/utils/types.ts b/src/utils/types.ts new file mode 100644 index 00000000..09e37baf --- /dev/null +++ b/src/utils/types.ts @@ -0,0 +1,4 @@ +import { StoreEnhancer } from 'redux' +import { StateSyncOptions } from '../options/StateSyncOptions' + +export type StateSyncEnhancer = (options: StateSyncOptions) => StoreEnhancer diff --git a/tests/e2e.spec.ts b/tests/e2e.spec.ts index f0653a3c..4611f4d3 100644 --- a/tests/e2e.spec.ts +++ b/tests/e2e.spec.ts @@ -14,11 +14,14 @@ describe('End to End Tests', () => { startTimeout: 5000, host: process.env.CHROMEDRIVER_HOST || 'localhost', port: parseInt(process.env.CHROMEDRIVER_PORT || '9222'), + env: { + SPECTRON: 'true', + }, }) await app.start() - // eslint-disable-next-line @typescript-eslint/await-thenable - await app.browserWindow.isVisible() + + await app.client.waitUntilWindowLoaded() }) afterEach(async () => { diff --git a/tests/fetchState.spec.ts b/tests/fetchState.spec.ts index 61892e6a..0186607f 100644 --- a/tests/fetchState.spec.ts +++ b/tests/fetchState.spec.ts @@ -1,4 +1,6 @@ -import { fetchInitialState, fetchInitialStateAsync } from '../src/fetchState' +import { fetchInitialStateAsync } from '../src/renderer/fetchInitialStateAsync' +import { fetchInitialState } from '../src/renderer/fetchInitialState' + import { IPCEvents } from '../src/constants' jest.mock('electron', () => ({ ipcRenderer: { diff --git a/tests/typescript/composeWithStateSync.ts b/tests/typescript/composeWithStateSync.ts index ea517e7c..9ea26612 100644 --- a/tests/typescript/composeWithStateSync.ts +++ b/tests/typescript/composeWithStateSync.ts @@ -1,24 +1,15 @@ -import { composeWithStateSync } from '../../types' -import { applyMiddleware, createStore, Store, StoreEnhancer } from 'redux' -import { reducer, CounterState, Actions } from '../counter' +import { applyMiddleware, createStore } from 'redux' +import { reducer } from '../counter' import { countMiddleware } from '../middleware' +import { composeWithStateSync } from '../../main' -// This is just a dummy enhancer, this does nothing -const someOtherEnhancer: StoreEnhancer = (next) => { - return (reducer, state) => { - return next(reducer, state) - } -} +const middleware = [countMiddleware] -const middleware = applyMiddleware(countMiddleware) +const _store1 = createStore(reducer, composeWithStateSync(applyMiddleware(...middleware))) -const enhancerWithoutOptions: StoreEnhancer = composeWithStateSync(middleware, someOtherEnhancer) +const _store2 = createStore(reducer, composeWithStateSync({}, applyMiddleware(...middleware))) -const store: Store = createStore(reducer, enhancerWithoutOptions) - -const enhancerWithOptions: StoreEnhancer = composeWithStateSync({ denyList: [] })( - middleware, - someOtherEnhancer +const _store3 = createStore( + reducer, + composeWithStateSync({ denyList: [] }, applyMiddleware(...middleware)) ) - -const store2: Store = createStore(reducer, enhancerWithOptions) diff --git a/tests/typescript/mainStateSyncEnhancer.ts b/tests/typescript/mainStateSyncEnhancer.ts index 60fe3541..0f5d51b9 100644 --- a/tests/typescript/mainStateSyncEnhancer.ts +++ b/tests/typescript/mainStateSyncEnhancer.ts @@ -1,5 +1,5 @@ -import { mainStateSyncEnhancer } from '../../types' +import { stateSyncEnhancer } from '../../main' import { createStore, Store } from 'redux' import { reducer, CounterState, Actions } from '../counter' -const store: Store = createStore(reducer, mainStateSyncEnhancer()) +const store: Store = createStore(reducer, stateSyncEnhancer()) diff --git a/tests/typescript/rendererStateSyncEnhancer.ts b/tests/typescript/rendererStateSyncEnhancer.ts index 3199b66d..76975f86 100644 --- a/tests/typescript/rendererStateSyncEnhancer.ts +++ b/tests/typescript/rendererStateSyncEnhancer.ts @@ -1,5 +1,9 @@ -import { rendererStateSyncEnhancer } from '../../types' +import { stateSyncEnhancer } from '../../renderer' import { createStore, Store } from 'redux' import { reducer, CounterState, Actions } from '../counter' -const store: Store = createStore(reducer, rendererStateSyncEnhancer()) +const store: Store = createStore(reducer, stateSyncEnhancer()) +const store2: Store = createStore( + reducer, + stateSyncEnhancer({ denyList: [] }) +) diff --git a/yarn.lock b/yarn.lock index d54d8d47..54d8004f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1353,18 +1353,6 @@ magic-string "^0.25.7" resolve "^1.17.0" -"@rollup/plugin-node-resolve@^9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-9.0.0.tgz#39bd0034ce9126b39c1699695f440b4b7d2b62e6" - integrity sha512-gPz+utFHLRrd41WMP13Jq5mqqzHL3OXrfj3/MkSyB6UBIcuNt9j60GCbarzMzdf1VHFpOxfQh/ez7wyadLMqkg== - dependencies: - "@rollup/pluginutils" "^3.1.0" - "@types/resolve" "1.17.1" - builtin-modules "^3.1.0" - deepmerge "^4.2.2" - is-module "^1.0.0" - resolve "^1.17.0" - "@rollup/pluginutils@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" @@ -1682,13 +1670,6 @@ dependencies: "@types/node" "*" -"@types/resolve@1.17.1": - version "1.17.1" - resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" - integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== - dependencies: - "@types/node" "*" - "@types/responselike@*", "@types/responselike@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" @@ -2467,11 +2448,6 @@ buffer@^5.2.1, buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" -builtin-modules@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484" - integrity sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw== - builtins@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" @@ -5380,11 +5356,6 @@ is-installed-globally@^0.1.0: global-dirs "^0.1.0" is-path-inside "^1.0.0" -is-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" - integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= - is-negative-zero@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461"