Skip to content

Commit 054b3e7

Browse files
committed
feat: withDevTools disabled in prod, and tree-shaking docs - add injection token config and improve docs after review
1 parent 27d146e commit 054b3e7

File tree

6 files changed

+81
-23
lines changed

6 files changed

+81
-23
lines changed

README.md

+16-2
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,27 @@ patchState(this.store, {loading: false});
7676
updateState(this.store 'update loading', {loading: false});
7777
```
7878

79-
`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.
79+
### Configuration
80+
81+
You may configure this feature by setting up a provider in your application:
82+
83+
```typescript
84+
providers: [
85+
provideStoreDevtoolsConfig({
86+
logOnly: !isDevMode(), // Restrict extension to log-only mode, default is false
87+
})
88+
]
89+
```
90+
91+
When running in production you may opt to tree-shake it from the application bundle you need to abstract it in your environment file.
8092

8193
<details>
8294

8395
<summary>Devtools tree-shaking details</summary>
8496

85-
environment.ts:
97+
If the environment files don't exist in your project you can use the `ng generate environments` command to create them.
98+
99+
environment.ts:
86100
```typescript
87101
import { withDevtools } from '@angular-architects/ngrx-toolkit';
88102

libs/ngrx-toolkit/src/index.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
export {
2-
withDevtools,
3-
patchState,
4-
updateState,
5-
Action,
6-
} from './lib/with-devtools';
1+
export * from './lib/with-devtools';
72
export * from './lib/with-redux';
83

94
export * from './lib/with-call-state';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export * from './with-devtools.config';
2+
export {
3+
withDevtools,
4+
patchState,
5+
updateState,
6+
Action,
7+
} from './with-devtools';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { InjectionToken } from '@angular/core';
2+
3+
export type DevtoolsConfig = {
4+
logOnly: boolean;
5+
};
6+
7+
export const DEFAULT_DEVTOOLS_CONFIG: DevtoolsConfig = {
8+
logOnly: false,
9+
};
10+
11+
export const DEVTOOLS_CONFIG = new InjectionToken<DevtoolsConfig>('DEVTOOLS_CONFIG');
12+
13+
14+
/**
15+
* Provide a custom configuration for the devtools.
16+
* @param config The custom configuration.
17+
*
18+
* @example
19+
* ```ts
20+
* provideStoreDevtoolsConfig({
21+
* logOnly: !isDevMode(), // Enable the log-only mode when not in production. (Default: false)
22+
* });
23+
* ```
24+
*
25+
* @returns The provider of the custom configuration.
26+
*/
27+
export const provideStoreDevtoolsConfig = (config: Partial<DevtoolsConfig>) => {
28+
return {
29+
provide: DEVTOOLS_CONFIG,
30+
useValue: {
31+
...DEFAULT_DEVTOOLS_CONFIG,
32+
...config,
33+
} satisfies DevtoolsConfig,
34+
};
35+
}

libs/ngrx-toolkit/src/lib/with-devtools.spec.ts libs/ngrx-toolkit/src/lib/with-devtools/with-devtools.spec.ts

+17-12
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
let isDevMode = true;
2-
jest.mock('@angular/core', () => ({ ...jest.requireActual('@angular/core'), isDevMode: () => isDevMode }))
3-
41
import { signalStore } from '@ngrx/signals';
52
import { withEntities } from '@ngrx/signals/entities';
6-
import { Action, withDevtools } from './with-devtools';
73
import { TestBed } from '@angular/core/testing';
84
import { PLATFORM_ID } from '@angular/core';
95
import SpyInstance = jest.SpyInstance;
106
import Mock = jest.Mock;
11-
import { reset } from './with-devtools';
7+
import { Action, withDevtools, reset } from './with-devtools';
8+
import { DevtoolsConfig, provideStoreDevtoolsConfig } from './with-devtools.config';
129

1310
type Flight = {
1411
id: number;
@@ -34,7 +31,7 @@ const createFlight = (flight: Partial<Flight> = {}) => ({
3431
interface SetupOptions {
3532
extensionsAvailable: boolean;
3633
inSsr: boolean;
37-
isDevMode: boolean
34+
config: DevtoolsConfig | null;
3835
}
3936

4037
interface TestData {
@@ -52,12 +49,10 @@ function run(
5249
const defaultOptions: SetupOptions = {
5350
inSsr: false,
5451
extensionsAvailable: true,
55-
isDevMode: true,
52+
config: null,
5653
};
5754
const realOptions = { ...defaultOptions, ...options };
5855

59-
isDevMode = realOptions.isDevMode;
60-
6156
const sendSpy = jest.fn<void, [Action, Record<string, unknown>]>();
6257
const connection = {
6358
send: sendSpy,
@@ -71,6 +66,7 @@ function run(
7166
provide: PLATFORM_ID,
7267
useValue: realOptions.inSsr ? 'server' : 'browser',
7368
},
69+
...(realOptions.config ? [provideStoreDevtoolsConfig(realOptions.config)] : []),
7470
],
7571
});
7672

@@ -124,12 +120,22 @@ describe('Devtools', () => {
124120
);
125121

126122
it(
127-
'should not connect if it runs in production',
123+
'should not connect if it runs in logOnly mode',
128124
run(
129125
({ connectSpy }) => {
130126
expect(connectSpy).toHaveBeenCalledTimes(0);
131127
},
132-
{ isDevMode: false }
128+
{ config: { logOnly: true } }
129+
)
130+
);
131+
132+
it(
133+
'should connect if it runs with config and logOnly=false',
134+
run(
135+
({ connectSpy }) => {
136+
expect(connectSpy).toHaveBeenCalledTimes(1);
137+
},
138+
{ config: { logOnly: false } }
133139
)
134140
);
135141

@@ -170,5 +176,4 @@ describe('Devtools', () => {
170176
it.todo('should patchState with action name');
171177
it.todo('should use patchState with default action name');
172178
it.todo('should group multiple patchStates (glitch-free) in one action');
173-
it.todo('should not run if in prod mode');
174179
});

libs/ngrx-toolkit/src/lib/with-devtools.ts libs/ngrx-toolkit/src/lib/with-devtools/with-devtools.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import {
55
SignalStoreFeature,
66
WritableStateSource,
77
} from '@ngrx/signals';
8-
import { effect, inject, isDevMode, PLATFORM_ID, signal, Signal } from '@angular/core';
8+
import { effect, inject, PLATFORM_ID, signal, Signal } from '@angular/core';
99
import { isPlatformServer } from '@angular/common';
10-
import { Prettify } from './shared/prettify';
10+
import { Prettify } from '../shared/prettify';
11+
import { DEFAULT_DEVTOOLS_CONFIG, DEVTOOLS_CONFIG } from './with-devtools.config';
1112

1213
declare global {
1314
interface Window {
@@ -87,7 +88,8 @@ export function withDevtools<Input extends EmptyFeatureResult>(
8788
): SignalStoreFeature<Input, EmptyFeatureResult> {
8889
return (store) => {
8990
const isServer = isPlatformServer(inject(PLATFORM_ID));
90-
if (isServer || !isDevMode()) {
91+
const { logOnly } = inject(DEVTOOLS_CONFIG, { optional: true }) || DEFAULT_DEVTOOLS_CONFIG;
92+
if (isServer || logOnly) {
9193
return store;
9294
}
9395

0 commit comments

Comments
 (0)