@blac/react 2.0.0-rc-16 → 2.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -0
- package/dist/index.cjs +13 -0
- package/{src/index.ts → dist/index.d.ts} +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/useBloc.d.ts +20 -0
- package/dist/useBloc.d.ts.map +1 -0
- package/dist/useBloc.js +173 -0
- package/dist/useBloc.js.map +1 -0
- package/dist/useExternalBlocStore.d.ts +31 -0
- package/dist/useExternalBlocStore.d.ts.map +1 -0
- package/dist/useExternalBlocStore.js +93 -0
- package/dist/useExternalBlocStore.js.map +1 -0
- package/package.json +26 -10
- package/src/__tests__/rerenderLogging.test.tsx +0 -257
- package/src/__tests__/useBloc.disposal.test.tsx +0 -377
- package/src/__tests__/useBloc.instanceChange.test.tsx +0 -200
- package/src/__tests__/useBloc.props.test.tsx +0 -435
- package/src/__tests__/useBloc.proxyConfig.test.tsx +0 -228
- package/src/__tests__/useBloc.staticProps.test.tsx +0 -297
- package/src/__tests__/useBloc.tracking.test.tsx +0 -202
- package/src/useBloc.ts +0 -227
- package/src/useExternalBlocStore.ts +0 -145
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = useExternalBlocStore;
|
|
4
|
+
const core_1 = require("@blac/core");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
/**
|
|
7
|
+
* Hook for using an external bloc store
|
|
8
|
+
* Provides low-level access to bloc instances for use with React's useSyncExternalStore
|
|
9
|
+
*/
|
|
10
|
+
function useExternalBlocStore(blocConstructor, options = {}) {
|
|
11
|
+
const ridRef = (0, react_1.useRef)(`external-${(0, core_1.generateUUID)()}`);
|
|
12
|
+
const usedKeysRef = (0, react_1.useRef)(new Set());
|
|
13
|
+
const usedClassPropKeysRef = (0, react_1.useRef)(new Set());
|
|
14
|
+
const instanceRef = (0, react_1.useRef)(null);
|
|
15
|
+
// Get or create bloc instance
|
|
16
|
+
const bloc = (0, react_1.useMemo)(() => {
|
|
17
|
+
const blac = core_1.Blac.getInstance();
|
|
18
|
+
const base = blocConstructor;
|
|
19
|
+
// For isolated blocs, always create a new instance
|
|
20
|
+
if (base.constructor.isolated ||
|
|
21
|
+
blocConstructor.isolated) {
|
|
22
|
+
const newBloc = new blocConstructor(options?.staticProps);
|
|
23
|
+
const uniqueId = options?.id || `${blocConstructor.name}_${(0, core_1.generateUUID)()}`;
|
|
24
|
+
newBloc._id = uniqueId;
|
|
25
|
+
blac.activateBloc(newBloc);
|
|
26
|
+
return newBloc;
|
|
27
|
+
}
|
|
28
|
+
// For shared blocs, use the existing getBloc logic
|
|
29
|
+
return blac.getBloc(blocConstructor, {
|
|
30
|
+
id: options?.id,
|
|
31
|
+
constructorParams: options?.staticProps,
|
|
32
|
+
});
|
|
33
|
+
}, [blocConstructor, options?.id]);
|
|
34
|
+
// Create external store interface
|
|
35
|
+
const externalStore = (0, react_1.useMemo)(() => ({
|
|
36
|
+
getSnapshot: () => {
|
|
37
|
+
const currentInstance = instanceRef.current;
|
|
38
|
+
return currentInstance ? currentInstance.state : undefined;
|
|
39
|
+
},
|
|
40
|
+
subscribe: (listener) => {
|
|
41
|
+
const currentInstance = instanceRef.current;
|
|
42
|
+
if (!currentInstance) {
|
|
43
|
+
// Return no-op unsubscribe if no instance
|
|
44
|
+
return () => { };
|
|
45
|
+
}
|
|
46
|
+
// Wrap listener to handle errors gracefully
|
|
47
|
+
const safeListener = (newState, oldState) => {
|
|
48
|
+
try {
|
|
49
|
+
// Reset tracking keys on each listener call
|
|
50
|
+
usedKeysRef.current.clear();
|
|
51
|
+
usedClassPropKeysRef.current.clear();
|
|
52
|
+
// Call selector if provided
|
|
53
|
+
if (options.selector && currentInstance) {
|
|
54
|
+
options.selector(newState, oldState, currentInstance);
|
|
55
|
+
}
|
|
56
|
+
// Call listener with state if it expects it
|
|
57
|
+
if (listener.length > 0) {
|
|
58
|
+
listener(newState);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
listener();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
console.error('Listener error in useExternalBlocStore:', error);
|
|
66
|
+
// Don't rethrow to prevent breaking other listeners
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
// Store the previous state for the listener
|
|
70
|
+
let previousState = currentInstance.state;
|
|
71
|
+
const unsubscribe = currentInstance.subscribe((state) => {
|
|
72
|
+
const oldState = previousState;
|
|
73
|
+
previousState = state;
|
|
74
|
+
safeListener(state, oldState);
|
|
75
|
+
});
|
|
76
|
+
return unsubscribe;
|
|
77
|
+
},
|
|
78
|
+
getServerSnapshot: () => {
|
|
79
|
+
const currentInstance = instanceRef.current;
|
|
80
|
+
return currentInstance ? currentInstance.state : undefined;
|
|
81
|
+
},
|
|
82
|
+
}), []);
|
|
83
|
+
// Update instance ref
|
|
84
|
+
instanceRef.current = bloc;
|
|
85
|
+
return {
|
|
86
|
+
externalStore,
|
|
87
|
+
instance: instanceRef,
|
|
88
|
+
usedKeys: usedKeysRef,
|
|
89
|
+
usedClassPropKeys: usedClassPropKeysRef,
|
|
90
|
+
rid: ridRef.current,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=useExternalBlocStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useExternalBlocStore.js","sourceRoot":"","sources":["../src/useExternalBlocStore.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blac/react",
|
|
3
|
-
"version": "2.0.0-rc
|
|
3
|
+
"version": "2.0.0-rc.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Brendan Mullins <jsnanigans@gmail.com>",
|
|
6
|
-
"main": "
|
|
7
|
-
"module": "
|
|
8
|
-
"
|
|
9
|
-
"
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": {
|
|
13
|
+
"development": "./src/index.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"require": "./dist/index.cjs",
|
|
17
|
+
"source": "./src/index.ts"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
10
20
|
"files": [
|
|
11
|
-
"
|
|
21
|
+
"dist",
|
|
12
22
|
"README.md",
|
|
13
23
|
"LICENSE"
|
|
14
24
|
],
|
|
@@ -26,10 +36,8 @@
|
|
|
26
36
|
"dependencies": {},
|
|
27
37
|
"peerDependencies": {
|
|
28
38
|
"@types/react": "^18.0.0 || ^19.0.0",
|
|
29
|
-
"@types/react-dom": "^18.0.0 || ^19.0.0",
|
|
30
39
|
"react": "^18.0.0 || ^19.0.0",
|
|
31
|
-
"
|
|
32
|
-
"@blac/core": "2.0.0-rc-15"
|
|
40
|
+
"@blac/core": "2.0.0-rc.1"
|
|
33
41
|
},
|
|
34
42
|
"peerDependenciesMeta": {
|
|
35
43
|
"@types/react": {
|
|
@@ -55,10 +63,18 @@
|
|
|
55
63
|
"react-dom": "^19.1.1",
|
|
56
64
|
"typescript": "^5.9.2",
|
|
57
65
|
"vitest": "^3.2.4",
|
|
58
|
-
"@blac/plugin-render-logging": "2.0.0-rc
|
|
66
|
+
"@blac/plugin-render-logging": "2.0.0-rc.1",
|
|
67
|
+
"@blac/core": "2.0.0-rc.1"
|
|
59
68
|
},
|
|
60
69
|
"scripts": {
|
|
70
|
+
"dev": "tsc --watch --project tsconfig.build.json",
|
|
71
|
+
"build": "pnpm run clean && pnpm run build:esm && pnpm run build:cjs",
|
|
72
|
+
"build:esm": "tsc --project tsconfig.build.json",
|
|
73
|
+
"build:cjs": "tsc --project tsconfig.build.json --module commonjs --outDir dist --declaration false --declarationMap false && mv dist/index.js dist/index.cjs",
|
|
74
|
+
"clean": "rm -rf dist",
|
|
61
75
|
"format": "prettier --write \".\"",
|
|
76
|
+
"lint": "eslint src --ext .ts,.tsx",
|
|
77
|
+
"lint:fix": "eslint src --ext .ts,.tsx --fix",
|
|
62
78
|
"test": "vitest run --config vitest.config.ts",
|
|
63
79
|
"test:watch": "vitest --watch --config vitest.config.ts",
|
|
64
80
|
"typecheck": "tsc --noEmit",
|
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { renderHook, act } from '@testing-library/react';
|
|
3
|
-
import { Blac, Cubit, RerenderLogger } from '@blac/core';
|
|
4
|
-
import { RenderLoggingPlugin } from '@blac/plugin-render-logging';
|
|
5
|
-
import useBloc from '../useBloc';
|
|
6
|
-
|
|
7
|
-
class TestCubit extends Cubit<{ count: number; name: string }> {
|
|
8
|
-
constructor() {
|
|
9
|
-
super({ count: 0, name: 'test' });
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
increment = () => this.emit({ ...this.state, count: this.state.count + 1 });
|
|
13
|
-
changeName = (name: string) => this.emit({ ...this.state, name });
|
|
14
|
-
changeAll = () => this.emit({ count: this.state.count + 1, name: 'changed' });
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
describe('Rerender Logging', () => {
|
|
18
|
-
let consoleSpy: any;
|
|
19
|
-
let plugin: RenderLoggingPlugin;
|
|
20
|
-
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
Blac.resetInstance();
|
|
23
|
-
RerenderLogger.clear();
|
|
24
|
-
consoleSpy = {
|
|
25
|
-
log: vi.spyOn(console, 'log').mockImplementation(() => {}),
|
|
26
|
-
group: vi.spyOn(console, 'group').mockImplementation(() => {}),
|
|
27
|
-
groupEnd: vi.spyOn(console, 'groupEnd').mockImplementation(() => {}),
|
|
28
|
-
table: vi.spyOn(console, 'table').mockImplementation(() => {}),
|
|
29
|
-
};
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
afterEach(() => {
|
|
33
|
-
consoleSpy.log.mockRestore();
|
|
34
|
-
consoleSpy.group.mockRestore();
|
|
35
|
-
consoleSpy.groupEnd.mockRestore();
|
|
36
|
-
consoleSpy.table.mockRestore();
|
|
37
|
-
// Remove plugin if it was added
|
|
38
|
-
if (plugin) {
|
|
39
|
-
Blac.getInstance().plugins.remove(plugin.name);
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('should not log when rerender logging is disabled', () => {
|
|
44
|
-
// No plugin registered
|
|
45
|
-
const { result } = renderHook(() => useBloc(TestCubit));
|
|
46
|
-
|
|
47
|
-
act(() => {
|
|
48
|
-
result.current[1].increment();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
expect(consoleSpy.log).not.toHaveBeenCalled();
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('should log rerenders when enabled with minimal level', () => {
|
|
55
|
-
plugin = new RenderLoggingPlugin({ enabled: true, level: 'minimal' });
|
|
56
|
-
Blac.getInstance().plugins.add(plugin);
|
|
57
|
-
|
|
58
|
-
const { result } = renderHook(() => useBloc(TestCubit));
|
|
59
|
-
|
|
60
|
-
// Clear logs from mount
|
|
61
|
-
consoleSpy.log.mockClear();
|
|
62
|
-
|
|
63
|
-
act(() => {
|
|
64
|
-
result.current[1].increment();
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
expect(consoleSpy.log).toHaveBeenCalled();
|
|
68
|
-
const logCall = consoleSpy.log.mock.calls[0][0];
|
|
69
|
-
expect(logCall).toContain('#2'); // Second render (first is mount)
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should log state changes with normal level', () => {
|
|
73
|
-
plugin = new RenderLoggingPlugin({ enabled: true, level: 'normal' });
|
|
74
|
-
Blac.getInstance().plugins.add(plugin);
|
|
75
|
-
|
|
76
|
-
const { result } = renderHook(() => useBloc(TestCubit));
|
|
77
|
-
|
|
78
|
-
// Clear logs from mount
|
|
79
|
-
consoleSpy.log.mockClear();
|
|
80
|
-
|
|
81
|
-
act(() => {
|
|
82
|
-
result.current[1].increment();
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
expect(consoleSpy.log).toHaveBeenCalled();
|
|
86
|
-
const logCall = consoleSpy.log.mock.calls[0];
|
|
87
|
-
expect(logCall.join(' ')).toContain('State changed');
|
|
88
|
-
expect(logCall.join(' ')).toContain('(entire state)'); // Without proxy tracking individual properties
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('should log detailed information with detailed level', () => {
|
|
92
|
-
plugin = new RenderLoggingPlugin({ enabled: true, level: 'detailed' });
|
|
93
|
-
Blac.getInstance().plugins.add(plugin);
|
|
94
|
-
|
|
95
|
-
const { result } = renderHook(() => useBloc(TestCubit));
|
|
96
|
-
|
|
97
|
-
// Clear logs from mount
|
|
98
|
-
consoleSpy.log.mockClear();
|
|
99
|
-
consoleSpy.group.mockClear();
|
|
100
|
-
consoleSpy.table.mockClear();
|
|
101
|
-
|
|
102
|
-
act(() => {
|
|
103
|
-
result.current[1].increment();
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
expect(consoleSpy.group).toHaveBeenCalled();
|
|
107
|
-
expect(consoleSpy.table).toHaveBeenCalled();
|
|
108
|
-
|
|
109
|
-
const tableData = consoleSpy.table.mock.calls[0][0];
|
|
110
|
-
// Since we haven't accessed specific properties, it shows '(entire state)'
|
|
111
|
-
expect(tableData).toHaveProperty('(entire state)');
|
|
112
|
-
expect(tableData['(entire state)']).toHaveProperty('old');
|
|
113
|
-
expect(tableData['(entire state)']).toHaveProperty('new');
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('should track dependency changes when using manual dependencies', () => {
|
|
117
|
-
plugin = new RenderLoggingPlugin({ enabled: true, level: 'normal' });
|
|
118
|
-
Blac.getInstance().plugins.add(plugin);
|
|
119
|
-
|
|
120
|
-
let depValue = 1;
|
|
121
|
-
const { result, rerender } = renderHook(() =>
|
|
122
|
-
useBloc(TestCubit, {
|
|
123
|
-
dependencies: () => [depValue],
|
|
124
|
-
}),
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
// Clear logs from mount
|
|
128
|
-
consoleSpy.log.mockClear();
|
|
129
|
-
|
|
130
|
-
// Change dependency
|
|
131
|
-
depValue = 2;
|
|
132
|
-
rerender();
|
|
133
|
-
|
|
134
|
-
expect(consoleSpy.log).toHaveBeenCalled();
|
|
135
|
-
const logCall = consoleSpy.log.mock.calls[0];
|
|
136
|
-
const logMessage = logCall.join(' ');
|
|
137
|
-
|
|
138
|
-
// The plugin logs "Manual dependencies changed" in the description
|
|
139
|
-
expect(logMessage).toContain('Manual dependencies changed');
|
|
140
|
-
// Verify it's logged as a dependency change (check for the emoji)
|
|
141
|
-
expect(logMessage).toContain('🔗');
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('should respect filter function', () => {
|
|
145
|
-
plugin = new RenderLoggingPlugin({
|
|
146
|
-
enabled: true,
|
|
147
|
-
filter: ({ blocName }) => blocName !== 'TestCubit',
|
|
148
|
-
});
|
|
149
|
-
Blac.getInstance().plugins.add(plugin);
|
|
150
|
-
|
|
151
|
-
const { result } = renderHook(() => useBloc(TestCubit));
|
|
152
|
-
|
|
153
|
-
act(() => {
|
|
154
|
-
result.current[1].increment();
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
expect(consoleSpy.log).not.toHaveBeenCalled();
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('should group consecutive rerenders when groupRerenders is enabled', async () => {
|
|
161
|
-
plugin = new RenderLoggingPlugin({
|
|
162
|
-
enabled: true,
|
|
163
|
-
groupRerenders: true,
|
|
164
|
-
});
|
|
165
|
-
Blac.getInstance().plugins.add(plugin);
|
|
166
|
-
|
|
167
|
-
const { result } = renderHook(() => useBloc(TestCubit));
|
|
168
|
-
|
|
169
|
-
// Clear logs from mount
|
|
170
|
-
consoleSpy.log.mockClear();
|
|
171
|
-
consoleSpy.group.mockClear();
|
|
172
|
-
|
|
173
|
-
// Multiple rapid state changes - React may batch these
|
|
174
|
-
act(() => {
|
|
175
|
-
result.current[1].increment();
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
act(() => {
|
|
179
|
-
result.current[1].increment();
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
act(() => {
|
|
183
|
-
result.current[1].increment();
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
// Wait for debounce
|
|
187
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
188
|
-
|
|
189
|
-
expect(consoleSpy.group).toHaveBeenCalled();
|
|
190
|
-
const groupCall = consoleSpy.group.mock.calls[0][0];
|
|
191
|
-
// Should have at least 2 renders (React may batch some updates)
|
|
192
|
-
expect(groupCall).toMatch(/rendered \d+ times/);
|
|
193
|
-
// Verify it grouped multiple renders
|
|
194
|
-
expect(
|
|
195
|
-
parseInt(groupCall.match(/rendered (\d+) times/)?.[1] || '0'),
|
|
196
|
-
).toBeGreaterThanOrEqual(2);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('should track multiple state properties changes', () => {
|
|
200
|
-
plugin = new RenderLoggingPlugin({ enabled: true, level: 'normal' });
|
|
201
|
-
Blac.getInstance().plugins.add(plugin);
|
|
202
|
-
|
|
203
|
-
const { result } = renderHook(() => useBloc(TestCubit));
|
|
204
|
-
|
|
205
|
-
// Clear logs from mount
|
|
206
|
-
consoleSpy.log.mockClear();
|
|
207
|
-
|
|
208
|
-
// Change all properties
|
|
209
|
-
act(() => {
|
|
210
|
-
result.current[1].changeAll();
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
expect(consoleSpy.log).toHaveBeenCalled();
|
|
214
|
-
const logCall = consoleSpy.log.mock.calls[0].join(' ');
|
|
215
|
-
expect(logCall).toContain('(entire state)'); // Without proxy tracking, all properties changed
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
it('should show mount reason for first render', () => {
|
|
219
|
-
plugin = new RenderLoggingPlugin({ enabled: true });
|
|
220
|
-
Blac.getInstance().plugins.add(plugin);
|
|
221
|
-
|
|
222
|
-
renderHook(() => useBloc(TestCubit));
|
|
223
|
-
|
|
224
|
-
expect(consoleSpy.log).toHaveBeenCalled();
|
|
225
|
-
const logCall = consoleSpy.log.mock.calls[0];
|
|
226
|
-
expect(logCall.join(' ')).toContain('mount');
|
|
227
|
-
expect(logCall.join(' ')).toContain('Component mounted');
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it('should include stack trace in detailed mode when enabled', () => {
|
|
231
|
-
plugin = new RenderLoggingPlugin({
|
|
232
|
-
enabled: true,
|
|
233
|
-
level: 'detailed',
|
|
234
|
-
includeStackTrace: true,
|
|
235
|
-
});
|
|
236
|
-
Blac.getInstance().plugins.add(plugin);
|
|
237
|
-
|
|
238
|
-
const { result } = renderHook(() => useBloc(TestCubit));
|
|
239
|
-
|
|
240
|
-
// Clear logs from mount
|
|
241
|
-
consoleSpy.log.mockClear();
|
|
242
|
-
consoleSpy.group.mockClear();
|
|
243
|
-
|
|
244
|
-
act(() => {
|
|
245
|
-
result.current[1].increment();
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
// In detailed mode with stack trace, it logs the stack
|
|
249
|
-
const logCalls = consoleSpy.log.mock.calls;
|
|
250
|
-
const hasStackTrace = logCalls.some((call: any[]) =>
|
|
251
|
-
call.some(
|
|
252
|
-
(arg: any) => typeof arg === 'string' && arg.includes('Stack trace:'),
|
|
253
|
-
),
|
|
254
|
-
);
|
|
255
|
-
expect(hasStackTrace).toBe(true);
|
|
256
|
-
});
|
|
257
|
-
});
|