UNPKG

14.7 kBJavaScriptView Raw
1(function (global, factory) {
2 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('jotai')) :
3 typeof define === 'function' && define.amd ? define(['exports', 'react', 'jotai'], factory) :
4 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.jotaiDevtools = {}, global.React, global.jotai));
5})(this, (function (exports, react, jotai) { 'use strict';
6
7 function useAtomDevtools(anAtom, options, deprecatedScope) {
8 if (typeof options === 'string' || typeof deprecatedScope !== 'undefined') {
9 console.warn('DEPRECATED [useAtomDevtools] use DevtoolOptions');
10 options = {
11 name: options,
12 scope: deprecatedScope
13 };
14 }
15
16 var _ref = options || {},
17 enabled = _ref.enabled,
18 name = _ref.name,
19 scope = _ref.scope;
20
21 var extension;
22
23 try {
24 extension = (enabled != null ? enabled : true) && window.__REDUX_DEVTOOLS_EXTENSION__;
25 } catch (_unused) {}
26
27 if (!extension) {
28 if (enabled) {
29 console.warn('Please install/enable Redux devtools extension');
30 }
31 }
32
33 var _useAtom = jotai.useAtom(anAtom, scope),
34 value = _useAtom[0],
35 setValue = _useAtom[1];
36
37 var lastValue = react.useRef(value);
38 var isTimeTraveling = react.useRef(false);
39 var devtools = react.useRef();
40 var atomName = name || anAtom.debugLabel || anAtom.toString();
41 react.useEffect(function () {
42 if (!extension) {
43 return;
44 }
45
46 var setValueIfWritable = function setValueIfWritable(value) {
47 if (typeof setValue === 'function') {
48 setValue(value);
49 return;
50 }
51
52 console.warn('[Warn] you cannot do write operations (Time-travelling, etc) in read-only atoms\n', anAtom);
53 };
54
55 devtools.current = extension.connect({
56 name: atomName
57 });
58 var unsubscribe = devtools.current.subscribe(function (message) {
59 var _message$payload3, _message$payload4;
60
61 if (message.type === 'ACTION' && message.payload) {
62 try {
63 setValueIfWritable(JSON.parse(message.payload));
64 } catch (e) {
65 console.error('please dispatch a serializable value that JSON.parse() support\n', e);
66 }
67 } else if (message.type === 'DISPATCH' && message.state) {
68 var _message$payload, _message$payload2;
69
70 if (((_message$payload = message.payload) == null ? void 0 : _message$payload.type) === 'JUMP_TO_ACTION' || ((_message$payload2 = message.payload) == null ? void 0 : _message$payload2.type) === 'JUMP_TO_STATE') {
71 isTimeTraveling.current = true;
72 setValueIfWritable(JSON.parse(message.state));
73 }
74 } else if (message.type === 'DISPATCH' && ((_message$payload3 = message.payload) == null ? void 0 : _message$payload3.type) === 'COMMIT') {
75 var _devtools$current;
76
77 (_devtools$current = devtools.current) == null ? void 0 : _devtools$current.init(lastValue.current);
78 } else if (message.type === 'DISPATCH' && ((_message$payload4 = message.payload) == null ? void 0 : _message$payload4.type) === 'IMPORT_STATE') {
79 var _message$payload$next;
80
81 var computedStates = ((_message$payload$next = message.payload.nextLiftedState) == null ? void 0 : _message$payload$next.computedStates) || [];
82 computedStates.forEach(function (_ref2, index) {
83 var state = _ref2.state;
84
85 if (index === 0) {
86 var _devtools$current2;
87
88 (_devtools$current2 = devtools.current) == null ? void 0 : _devtools$current2.init(state);
89 } else {
90 setValueIfWritable(state);
91 }
92 });
93 }
94 });
95 devtools.current.shouldInit = true;
96 return unsubscribe;
97 }, [anAtom, extension, atomName, setValue]);
98 react.useEffect(function () {
99 if (!devtools.current) {
100 return;
101 }
102
103 lastValue.current = value;
104
105 if (devtools.current.shouldInit) {
106 devtools.current.init(value);
107 devtools.current.shouldInit = false;
108 } else if (isTimeTraveling.current) {
109 isTimeTraveling.current = false;
110 } else {
111 devtools.current.send(atomName + " - " + new Date().toLocaleString(), value);
112 }
113 }, [anAtom, extension, atomName, value]);
114 }
115
116 function _unsupportedIterableToArray(o, minLen) {
117 if (!o) return;
118 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
119 var n = Object.prototype.toString.call(o).slice(8, -1);
120 if (n === "Object" && o.constructor) n = o.constructor.name;
121 if (n === "Map" || n === "Set") return Array.from(o);
122 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
123 }
124
125 function _arrayLikeToArray(arr, len) {
126 if (len == null || len > arr.length) len = arr.length;
127
128 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
129
130 return arr2;
131 }
132
133 function _createForOfIteratorHelperLoose(o, allowArrayLike) {
134 var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
135 if (it) return (it = it.call(o)).next.bind(it);
136
137 if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
138 if (it) o = it;
139 var i = 0;
140 return function () {
141 if (i >= o.length) return {
142 done: true
143 };
144 return {
145 done: false,
146 value: o[i++]
147 };
148 };
149 }
150
151 throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
152 }
153
154 var RESTORE_ATOMS = 'h';
155 var DEV_SUBSCRIBE_STATE = 'n';
156 var DEV_GET_MOUNTED_ATOMS = 'l';
157 var DEV_GET_ATOM_STATE = 'a';
158 var DEV_GET_MOUNTED = 'm';
159
160 var createAtomsSnapshot = function createAtomsSnapshot(store, atoms) {
161 var tuples = atoms.map(function (atom) {
162 var _store$DEV_GET_ATOM_S, _store$DEV_GET_ATOM_S2;
163
164 var atomState = (_store$DEV_GET_ATOM_S = (_store$DEV_GET_ATOM_S2 = store[DEV_GET_ATOM_STATE]) == null ? void 0 : _store$DEV_GET_ATOM_S2.call(store, atom)) != null ? _store$DEV_GET_ATOM_S : {};
165 return [atom, 'v' in atomState ? atomState.v : undefined];
166 });
167 return new Map(tuples);
168 };
169
170 function useAtomsSnapshot(scope) {
171 var ScopeContext = jotai.SECRET_INTERNAL_getScopeContext(scope);
172 var scopeContainer = react.useContext(ScopeContext);
173 var store = scopeContainer.s;
174
175 if (!store[DEV_SUBSCRIBE_STATE]) {
176 throw new Error('useAtomsSnapshot can only be used in dev mode.');
177 }
178
179 var _useState = react.useState(function () {
180 return new Map();
181 }),
182 atomsSnapshot = _useState[0],
183 setAtomsSnapshot = _useState[1];
184
185 react.useEffect(function () {
186 var _store$DEV_SUBSCRIBE_;
187
188 var callback = function callback() {
189 var _store$DEV_GET_MOUNTE;
190
191 var atoms = Array.from(((_store$DEV_GET_MOUNTE = store[DEV_GET_MOUNTED_ATOMS]) == null ? void 0 : _store$DEV_GET_MOUNTE.call(store)) || []);
192 setAtomsSnapshot(createAtomsSnapshot(store, atoms));
193 };
194
195 var unsubscribe = (_store$DEV_SUBSCRIBE_ = store[DEV_SUBSCRIBE_STATE]) == null ? void 0 : _store$DEV_SUBSCRIBE_.call(store, callback);
196 callback();
197 return unsubscribe;
198 }, [store]);
199 return atomsSnapshot;
200 }
201
202 function useGotoAtomsSnapshot(scope) {
203 var ScopeContext = jotai.SECRET_INTERNAL_getScopeContext(scope);
204 var scopeContainer = react.useContext(ScopeContext);
205 var store = scopeContainer.s;
206
207 if (!store[DEV_SUBSCRIBE_STATE]) {
208 throw new Error('useGotoAtomsSnapshot can only be used in dev mode.');
209 }
210
211 return react.useCallback(function (values) {
212 store[RESTORE_ATOMS](values);
213 }, [store]);
214 }
215
216 var isEqualAtomsValues = function isEqualAtomsValues(left, right) {
217 return left.size === right.size && Array.from(left).every(function (_ref) {
218 var left = _ref[0],
219 v = _ref[1];
220 return Object.is(right.get(left), v);
221 });
222 };
223
224 var isEqualAtomsDependents = function isEqualAtomsDependents(left, right) {
225 return left.size === right.size && Array.from(left).every(function (_ref2) {
226 var a = _ref2[0],
227 dLeft = _ref2[1];
228 var dRight = right.get(a);
229 return dRight && dLeft.size === dRight.size && Array.from(dLeft).every(function (d) {
230 return dRight.has(d);
231 });
232 });
233 };
234
235 var atomToPrintable = function atomToPrintable(atom) {
236 return atom.debugLabel ? atom + ":" + atom.debugLabel : "" + atom;
237 };
238
239 var getDevtoolsState = function getDevtoolsState(atomsSnapshot) {
240 var values = {};
241 atomsSnapshot[0].forEach(function (v, atom) {
242 values[atomToPrintable(atom)] = v;
243 });
244 var dependents = {};
245 atomsSnapshot[1].forEach(function (d, atom) {
246 dependents[atomToPrintable(atom)] = Array.from(d).map(atomToPrintable);
247 });
248 return {
249 values: values,
250 dependents: dependents
251 };
252 };
253
254 function useAtomsDevtools(name, options) {
255 if (typeof options !== 'undefined' && typeof options !== 'object') {
256 console.warn('DEPRECATED [useAtomsDevtools] use DevtoolsOptions');
257 options = {
258 scope: options
259 };
260 }
261
262 var _ref3 = options || {},
263 enabled = _ref3.enabled,
264 scope = _ref3.scope;
265
266 var ScopeContext = jotai.SECRET_INTERNAL_getScopeContext(scope);
267
268 var _useContext = react.useContext(ScopeContext),
269 store = _useContext.s,
270 versionedWrite = _useContext.w;
271
272 var extension;
273
274 try {
275 extension = (enabled != null ? enabled : true) && window.__REDUX_DEVTOOLS_EXTENSION__;
276 } catch (_unused) {}
277
278 if (!extension) {
279 if (enabled) {
280 console.warn('Please install/enable Redux devtools extension');
281 }
282 }
283
284 if (extension && !store[DEV_SUBSCRIBE_STATE]) {
285 throw new Error('useAtomsDevtools can only be used in dev mode.');
286 }
287
288 var _useState = react.useState(function () {
289 return [new Map(), new Map()];
290 }),
291 atomsSnapshot = _useState[0],
292 setAtomsSnapshot = _useState[1];
293
294 react.useEffect(function () {
295 var _store$DEV_SUBSCRIBE_;
296
297 if (!extension) {
298 return;
299 }
300
301 var callback = function callback() {
302 var values = new Map();
303 var dependents = new Map();
304
305 for (var _iterator = _createForOfIteratorHelperLoose(((_store$DEV_GET_MOUNTE = store[DEV_GET_MOUNTED_ATOMS]) == null ? void 0 : _store$DEV_GET_MOUNTE.call(store)) || []), _step; !(_step = _iterator()).done;) {
306 var _store$DEV_GET_MOUNTE, _store$DEV_GET_ATOM_S, _store$DEV_GET_MOUNTE2;
307
308 var atom = _step.value;
309 var atomState = (_store$DEV_GET_ATOM_S = store[DEV_GET_ATOM_STATE]) == null ? void 0 : _store$DEV_GET_ATOM_S.call(store, atom);
310
311 if (atomState) {
312 if (atomState.r === atomState.i) {
313 return;
314 }
315
316 if ('v' in atomState) {
317 values.set(atom, atomState.v);
318 }
319 }
320
321 var mounted = (_store$DEV_GET_MOUNTE2 = store[DEV_GET_MOUNTED]) == null ? void 0 : _store$DEV_GET_MOUNTE2.call(store, atom);
322
323 if (mounted) {
324 dependents.set(atom, mounted.t);
325 }
326 }
327
328 setAtomsSnapshot(function (prev) {
329 if (isEqualAtomsValues(prev[0], values) && isEqualAtomsDependents(prev[1], dependents)) {
330 return prev;
331 }
332
333 return [values, dependents];
334 });
335 };
336
337 var unsubscribe = (_store$DEV_SUBSCRIBE_ = store[DEV_SUBSCRIBE_STATE]) == null ? void 0 : _store$DEV_SUBSCRIBE_.call(store, callback);
338 callback();
339 return unsubscribe;
340 }, [extension, store]);
341 var goToSnapshot = react.useCallback(function (values) {
342 if (versionedWrite) {
343 versionedWrite(function (version) {
344 store[RESTORE_ATOMS](values, version);
345 });
346 } else {
347 store[RESTORE_ATOMS](values);
348 }
349 }, [store, versionedWrite]);
350 var isTimeTraveling = react.useRef(false);
351 var isRecording = react.useRef(true);
352 var devtools = react.useRef();
353 var snapshots = react.useRef([]);
354 react.useEffect(function () {
355 if (!extension) {
356 return;
357 }
358
359 var getSnapshotAt = function getSnapshotAt(index) {
360 if (index === void 0) {
361 index = snapshots.current.length - 1;
362 }
363
364 var snapshot = snapshots.current[index >= 0 ? index : 0];
365
366 if (!snapshot) {
367 throw new Error('snaphost index out of bounds');
368 }
369
370 return snapshot;
371 };
372
373 var connection = extension.connect({
374 name: name
375 });
376 var devtoolsUnsubscribe = connection.subscribe(function (message) {
377 var _message$payload;
378
379 switch (message.type) {
380 case 'DISPATCH':
381 switch ((_message$payload = message.payload) == null ? void 0 : _message$payload.type) {
382 case 'RESET':
383 break;
384
385 case 'COMMIT':
386 connection.init(getDevtoolsState(getSnapshotAt()));
387 snapshots.current = [];
388 break;
389
390 case 'JUMP_TO_ACTION':
391 case 'JUMP_TO_STATE':
392 isTimeTraveling.current = true;
393 goToSnapshot(getSnapshotAt(message.payload.actionId - 1)[0]);
394 break;
395
396 case 'PAUSE_RECORDING':
397 isRecording.current = !isRecording.current;
398 break;
399 }
400
401 }
402 });
403 devtools.current = connection;
404 devtools.current.shouldInit = true;
405 return devtoolsUnsubscribe;
406 }, [extension, goToSnapshot, name]);
407 react.useEffect(function () {
408 if (!devtools.current) {
409 return;
410 }
411
412 if (devtools.current.shouldInit) {
413 devtools.current.init(undefined);
414 devtools.current.shouldInit = false;
415 return;
416 }
417
418 if (isTimeTraveling.current) {
419 isTimeTraveling.current = false;
420 } else if (isRecording.current) {
421 snapshots.current.push(atomsSnapshot);
422 devtools.current.send({
423 type: "" + snapshots.current.length,
424 updatedAt: new Date().toLocaleString()
425 }, getDevtoolsState(atomsSnapshot));
426 }
427 }, [atomsSnapshot]);
428 }
429
430 exports.useAtomDevtools = useAtomDevtools;
431 exports.useAtomsDevtools = useAtomsDevtools;
432 exports.useAtomsSnapshot = useAtomsSnapshot;
433 exports.useGotoAtomsSnapshot = useGotoAtomsSnapshot;
434
435 Object.defineProperty(exports, '__esModule', { value: true });
436
437}));