Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor/devtools #112

Merged
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
refactor(devtools): use features for better treeshaking
rainerhahnekamp committed Jan 4, 2025

Verified

This commit was signed with the committer’s verified signature.
Aaron1011 Aaron Hill
commit cda954818e729129506072e3b54fcf18e142d705
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ jobs:
node-version: '18'
cache: "pnpm"
- run: pnpm install --frozen-lockfile
- run: pnpm run test:all
- run: pnpm run build:all
- run: ./integration-tests.sh

54 changes: 0 additions & 54 deletions apps/demo/src/app/test.ts

This file was deleted.

1 change: 1 addition & 0 deletions libs/ngrx-toolkit/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { withDevToolsStub } from './lib/devtools/with-dev-tools-stub';
export { withDevtools } from './lib/devtools/with-devtools';
export { withDisabledNameIndices } from './lib/devtools/with-disabled-name-indicies';
export { patchState, updateState } from './lib/devtools/update-state';
export { renameDevtoolsName } from './lib/devtools/rename-devtools-name';

20 changes: 20 additions & 0 deletions libs/ngrx-toolkit/src/lib/devtools/devtools-feature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const DEVTOOLS_FEATURE = Symbol('DEVTOOLS_FEATURE');

/**
* A DevtoolsFeature adds or modifies the behavior of the
* devtools extension.
*
* We use them (function calls) instead of a config object,
* because of tree-shaking.
*/
export interface DevtoolsFeature {
[DEVTOOLS_FEATURE]: true;
indexNames: boolean | undefined; // defines if names should be indexed.
}

export function createDevtoolsFeature(indexNames = true): DevtoolsFeature {
return {
[DEVTOOLS_FEATURE]: true,
indexNames,
};
}
Original file line number Diff line number Diff line change
@@ -117,7 +117,7 @@ Enable automatic indexing via withDevTools('${storeName}', { indexNames: true })
this.#stores.update((stores) => {
if (newName in stores) {
throw new Error(
`NgRx Toolkit/DevTools: cannot rename from ${oldName} to ${newName}. ${oldName} does not exist.`
`NgRx Toolkit/DevTools: cannot rename from ${oldName} to ${newName}. ${newName} is already assigned to another SignalStore instance.`
);
}

14 changes: 8 additions & 6 deletions libs/ngrx-toolkit/src/lib/devtools/tests/naming.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { patchState, signalStore, withMethods, withState } from '@ngrx/signals';
import { signalStore, withState } from '@ngrx/signals';
import { withDevtools } from '../with-devtools';
import { TestBed, waitForAsync } from '@angular/core/testing';
import { TestBed } from '@angular/core/testing';
import { setupExtensions } from './helpers';
import {
createEnvironmentInjector,
@@ -9,6 +9,7 @@ import {
runInInjectionContext,
} from '@angular/core';
import { renameDevtoolsName } from '../rename-devtools-name';
import { withDisabledNameIndices } from '../with-disabled-name-indicies';

describe('withDevtools / renaming', () => {
it('should automatically index multiple instances', () => {
@@ -84,7 +85,7 @@ describe('withDevtools / renaming', () => {
setupExtensions();
const Store = signalStore(
{ providedIn: 'root' },
withDevtools('flights', { indexNames: false }),
withDevtools('flights', withDisabledNameIndices()),
withState({ airline: 'Lufthansa' })
);

@@ -103,7 +104,7 @@ Enable automatic indexing via withDevTools('flights', { indexNames: true }), or
});

it('should throw if name already exists', () => {
const { sendSpy } = setupExtensions();
setupExtensions();
signalStore(withDevtools('flights'));
expect(() => signalStore(withDevtools('flights'))).toThrow(
'The store "flights" has already been registered in the DevTools. Duplicate registration is not allowed.'
@@ -151,7 +152,7 @@ Enable automatic indexing via withDevTools('flights', { indexNames: true }), or

it('should throw on rename if name already exists', () => {
setupExtensions();
signalStore(
const Store1 = signalStore(
{ providedIn: 'root' },
withState({ name: 'Product', price: 10.5 }),
withDevtools('shop')
@@ -162,11 +163,12 @@ Enable automatic indexing via withDevTools('flights', { indexNames: true }), or
withState({ name: 'Product', price: 10.5 }),
withDevtools('mall')
);
TestBed.inject(Store1);
const store = TestBed.inject(Store2);
TestBed.flushEffects();

expect(() => renameDevtoolsName(store, 'shop')).toThrow(
'NgRx Toolkit/DevTools: cannot rename from mall to shop. mall has already been send to DevTools.'
'NgRx Toolkit/DevTools: cannot rename from mall to shop. shop is already assigned to another SignalStore instance.'
);
});

53 changes: 9 additions & 44 deletions libs/ngrx-toolkit/src/lib/devtools/with-devtools.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import {
SignalStoreFeature,
signalStoreFeature,
SignalStoreFeatureResult,
withHooks,
withMethods,
} from '@ngrx/signals';
import { signalStoreFeature, withHooks, withMethods } from '@ngrx/signals';
import { inject } from '@angular/core';
import { DevtoolsSyncer } from './internal/devtools-syncer.service';
import { DevtoolsFeature } from './devtools-feature';

export type Action = { type: string };
export type Connection = {
@@ -23,36 +18,6 @@ declare global {
}

export type DevtoolsOptions = {
/**
* If multiple instances of the same SignalStore class
* exist, their devtool names are indexed.
*
* If this feature is disabled, this feature throws
* a runtime error.
*
* By default, the value is `true`.
*
* For example:
* <pre>
* const Store = signalStore(
* withDevtools('flights', { indexNames: true })
* )
*
* const store1 = new Store(); // will show up as 'flights'
* const store2 = new Store(); // will show up as 'flights-1'
* </pre>
*
* With value set to `false`:
* <pre>
* const Store = signalStore(
* withDevtools('flights', { indexNames: false })
* )
*
* const store1 = new Store(); // will show up as 'flights'
* const store2 = new Store(); //💥 throws an error
* </pre>
*
*/
indexNames: boolean;
};

@@ -65,26 +30,26 @@ export const uniqueDevtoolsId = '___uniqueDevtoolsId';
* Adds this store as a feature state to the Redux DevTools.
*
* By default, the action name is 'Store Update'. You can
* change that via the `patch` method, which has as second
* change that via the {@link updateState} method, which has as second
* parameter the action name.
*
* The standalone function {@link renameDevtoolsName} can rename
* the store name.
*
* @param name name of the store as it should appear in the DevTools
* @param options options for the DevTools
* @param features features to extend or modify the behavior of the Devtools
*/
export function withDevtools(
name: string,
options: Partial<DevtoolsOptions> = {}
) {
export function withDevtools(name: string, ...features: DevtoolsFeature[]) {
if (existingNames.has(name)) {
throw new Error(
`The store "${name}" has already been registered in the DevTools. Duplicate registration is not allowed.`
);
}
existingNames.set(name, true);
const finalOptions: DevtoolsOptions = { ...{ indexNames: true }, ...options };
const finalOptions = {
indexNames: !features.some((f) => f.indexNames === false),
};

return signalStoreFeature(
withMethods((store) => {
const syncer = inject(DevtoolsSyncer);
30 changes: 30 additions & 0 deletions libs/ngrx-toolkit/src/lib/devtools/with-disabled-name-indicies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { createDevtoolsFeature } from './devtools-feature';

/**
* If multiple instances of the same SignalStore class
* exist, their devtool names are indexed.
*
* For example:
* <pre>
* const Store = signalStore(
* withDevtools('flights')
* )
*
* const store1 = new Store(); // will show up as 'flights'
* const store2 = new Store(); // will show up as 'flights-1'
* </pre>
*
* With adding `withDisabledNameIndices` to the store:
* <pre>
* const Store = signalStore(
* withDevtools('flights', withDisabledNameIndices())
* )
*
* const store1 = new Store(); // will show up as 'flights'
* const store2 = new Store(); //💥 throws an error
* </pre>
*
*/
export function withDisabledNameIndices() {
return createDevtoolsFeature(false);
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@
"scripts": {
"start": "nx serve demo",
"build:all": "nx run-many --targets=build",
"test:all": "nx run-many --targets=test",
"verify:all": "nx run-many --targets=lint,test,build"
},
"private": true,