diff --git a/README.md b/README.md index 68fe8f5..ac4b10b 100644 --- a/README.md +++ b/README.md @@ -76,13 +76,27 @@ patchState(this.store, {loading: false}); updateState(this.store 'update loading', {loading: false}); ``` -`withDevtools()` is by default disabled in production mode, however if you want to tree-shake it from the application bundle you need to abstract it in your environment file. +### Configuration + +You may configure this feature by setting up a provider in your application: + +```typescript +providers: [ + provideStoreDevtoolsConfig({ + logOnly: !isDevMode(), // Restrict extension to log-only mode, default is false + }) +] +``` + +When running in production you may opt to tree-shake it from the application bundle you need to abstract it in your environment file.
Devtools tree-shaking details - environment.ts: +If the environment files don't exist in your project you can use the `ng generate environments` command to create them. + +environment.ts: ```typescript import { withDevtools } from '@angular-architects/ngrx-toolkit'; diff --git a/libs/ngrx-toolkit/src/index.ts b/libs/ngrx-toolkit/src/index.ts index fe69e84..d7677d3 100644 --- a/libs/ngrx-toolkit/src/index.ts +++ b/libs/ngrx-toolkit/src/index.ts @@ -1,9 +1,4 @@ -export { - withDevtools, - patchState, - updateState, - Action, -} from './lib/with-devtools'; +export * from './lib/with-devtools'; export * from './lib/with-redux'; export * from './lib/with-call-state'; diff --git a/libs/ngrx-toolkit/src/lib/with-devtools/index.ts b/libs/ngrx-toolkit/src/lib/with-devtools/index.ts new file mode 100644 index 0000000..3d9dea3 --- /dev/null +++ b/libs/ngrx-toolkit/src/lib/with-devtools/index.ts @@ -0,0 +1,7 @@ +export * from './with-devtools.config'; +export { + withDevtools, + patchState, + updateState, + Action, +} from './with-devtools'; diff --git a/libs/ngrx-toolkit/src/lib/with-devtools/with-devtools.config.ts b/libs/ngrx-toolkit/src/lib/with-devtools/with-devtools.config.ts new file mode 100644 index 0000000..3cbcbd9 --- /dev/null +++ b/libs/ngrx-toolkit/src/lib/with-devtools/with-devtools.config.ts @@ -0,0 +1,35 @@ +import { InjectionToken } from '@angular/core'; + +export type DevtoolsConfig = { + logOnly: boolean; +}; + +export const DEFAULT_DEVTOOLS_CONFIG: DevtoolsConfig = { + logOnly: false, +}; + +export const DEVTOOLS_CONFIG = new InjectionToken('DEVTOOLS_CONFIG'); + + +/** + * Provide a custom configuration for the devtools. + * @param config The custom configuration. + * + * @example + * ```ts + * provideStoreDevtoolsConfig({ + * logOnly: !isDevMode(), // Enable the log-only mode when not in production. (Default: false) + * }); + * ``` + * + * @returns The provider of the custom configuration. + */ +export const provideStoreDevtoolsConfig = (config: Partial) => { + return { + provide: DEVTOOLS_CONFIG, + useValue: { + ...DEFAULT_DEVTOOLS_CONFIG, + ...config, + } satisfies DevtoolsConfig, + }; +} diff --git a/libs/ngrx-toolkit/src/lib/with-devtools.spec.ts b/libs/ngrx-toolkit/src/lib/with-devtools/with-devtools.spec.ts similarity index 86% rename from libs/ngrx-toolkit/src/lib/with-devtools.spec.ts rename to libs/ngrx-toolkit/src/lib/with-devtools/with-devtools.spec.ts index 869095b..5f0a4d7 100644 --- a/libs/ngrx-toolkit/src/lib/with-devtools.spec.ts +++ b/libs/ngrx-toolkit/src/lib/with-devtools/with-devtools.spec.ts @@ -1,14 +1,11 @@ -let isDevMode = true; -jest.mock('@angular/core', () => ({ ...jest.requireActual('@angular/core'), isDevMode: () => isDevMode })) - import { signalStore } from '@ngrx/signals'; import { withEntities } from '@ngrx/signals/entities'; -import { Action, withDevtools } from './with-devtools'; import { TestBed } from '@angular/core/testing'; import { PLATFORM_ID } from '@angular/core'; import SpyInstance = jest.SpyInstance; import Mock = jest.Mock; -import { reset } from './with-devtools'; +import { Action, withDevtools, reset } from './with-devtools'; +import { DevtoolsConfig, provideStoreDevtoolsConfig } from './with-devtools.config'; type Flight = { id: number; @@ -34,7 +31,7 @@ const createFlight = (flight: Partial = {}) => ({ interface SetupOptions { extensionsAvailable: boolean; inSsr: boolean; - isDevMode: boolean + config: DevtoolsConfig | null; } interface TestData { @@ -52,12 +49,10 @@ function run( const defaultOptions: SetupOptions = { inSsr: false, extensionsAvailable: true, - isDevMode: true, + config: null, }; const realOptions = { ...defaultOptions, ...options }; - isDevMode = realOptions.isDevMode; - const sendSpy = jest.fn]>(); const connection = { send: sendSpy, @@ -71,6 +66,7 @@ function run( provide: PLATFORM_ID, useValue: realOptions.inSsr ? 'server' : 'browser', }, + ...(realOptions.config ? [provideStoreDevtoolsConfig(realOptions.config)] : []), ], }); @@ -124,12 +120,22 @@ describe('Devtools', () => { ); it( - 'should not connect if it runs in production', + 'should not connect if it runs in logOnly mode', run( ({ connectSpy }) => { expect(connectSpy).toHaveBeenCalledTimes(0); }, - { isDevMode: false } + { config: { logOnly: true } } + ) + ); + + it( + 'should connect if it runs with config and logOnly=false', + run( + ({ connectSpy }) => { + expect(connectSpy).toHaveBeenCalledTimes(1); + }, + { config: { logOnly: false } } ) ); @@ -170,5 +176,4 @@ describe('Devtools', () => { it.todo('should patchState with action name'); it.todo('should use patchState with default action name'); it.todo('should group multiple patchStates (glitch-free) in one action'); - it.todo('should not run if in prod mode'); }); diff --git a/libs/ngrx-toolkit/src/lib/with-devtools.ts b/libs/ngrx-toolkit/src/lib/with-devtools/with-devtools.ts similarity index 91% rename from libs/ngrx-toolkit/src/lib/with-devtools.ts rename to libs/ngrx-toolkit/src/lib/with-devtools/with-devtools.ts index 34df2c6..b7fb789 100644 --- a/libs/ngrx-toolkit/src/lib/with-devtools.ts +++ b/libs/ngrx-toolkit/src/lib/with-devtools/with-devtools.ts @@ -5,9 +5,10 @@ import { SignalStoreFeature, WritableStateSource, } from '@ngrx/signals'; -import { effect, inject, isDevMode, PLATFORM_ID, signal, Signal } from '@angular/core'; +import { effect, inject, InjectionToken, isDevMode, PLATFORM_ID, signal, Signal } from '@angular/core'; import { isPlatformServer } from '@angular/common'; -import { Prettify } from './shared/prettify'; +import { Prettify } from '../shared/prettify'; +import { DEFAULT_DEVTOOLS_CONFIG, DEVTOOLS_CONFIG } from './with-devtools.config'; declare global { interface Window { @@ -87,7 +88,8 @@ export function withDevtools( ): SignalStoreFeature { return (store) => { const isServer = isPlatformServer(inject(PLATFORM_ID)); - if (isServer || !isDevMode()) { + const { logOnly } = inject(DEVTOOLS_CONFIG, { optional: true }) || DEFAULT_DEVTOOLS_CONFIG; + if (isServer || logOnly) { return store; }