diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 00000000..a3e67e02 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,21 @@ +interface ConfigMap { + enableHotModuleReload: boolean; +} +type ConfigKeys = keyof ConfigMap; + +function createGlobalConfig() { + const config: ConfigMap = { + enableHotModuleReload: false, + }; + + return { + setProperty: (key: Key, value: ConfigMap[Key]) => { + config[key] = value; + }, + getProperty: (key: Key) => { + return config[key]; + }, + }; +} + +export const rxBeachConfig = createGlobalConfig(); diff --git a/src/persistentReducedStream.ts b/src/persistentReducedStream.ts index 0f3fd2d2..acc90c33 100644 --- a/src/persistentReducedStream.ts +++ b/src/persistentReducedStream.ts @@ -6,6 +6,7 @@ import { ReducedStreamOptions, reducedStream as reducedStreamInternal, } from './internal/reducedStream'; +import { rxBeachConfig } from './config'; /** * Creates and registers a persistent reduced state stream @@ -48,9 +49,31 @@ export const persistentReducedStream = ( tag(name) ); - const stream = new ObservableState(name, source$, initialState); + try { + const stream = new ObservableState(name, source$, initialState); + stateStreamRegistry.register(stream); + return stream; + } catch (error) { + if ( + rxBeachConfig.getProperty('enableHotModuleReload') && + error instanceof Error && + error.message.startsWith('Duplicate stream name: ') + ) { + // The following enables Hot Module Reloading (HMR) of persistentReducedStream by + // handling the "duplicate stream" error that is thrown when reusing a stream name. + console.warn( + `[HMR] Replacing stream "${name}", if you see this warning when your ` + + `application boots the first time you have used the same name twice!` + ); + const prevState = + stateStreamRegistry.streams.get(name)?.state ?? initialState; + const newStream = new ObservableState(name, source$, prevState); - stateStreamRegistry.register(stream); + stateStreamRegistry.unregister(name); + stateStreamRegistry.register(newStream); + return newStream; + } - return stream; + throw error; + } }; diff --git a/src/stateStreamRegistry.ts b/src/stateStreamRegistry.ts index abfc4586..bd4fff03 100644 --- a/src/stateStreamRegistry.ts +++ b/src/stateStreamRegistry.ts @@ -27,6 +27,22 @@ export class StateStreamRegistry { this.streams.set(stream.name, stream); } + /** + * Unregister a stream from the registry + * + * @param name The name of the stream + * @returns void + */ + unregister(name: string) { + var stream = this.streams.get(name) + if (!stream) { + return; + } + + this.streams.delete(name) + stream.unsubscribe(); +}; + /** * Start the registered reduced state streams. *