UNPKG

20.3 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 _extends() {
8 _extends = Object.assign ? Object.assign.bind() : function (target) {
9 for (var i = 1; i < arguments.length; i++) {
10 var source = arguments[i];
11
12 for (var key in source) {
13 if (Object.prototype.hasOwnProperty.call(source, key)) {
14 target[key] = source[key];
15 }
16 }
17 }
18
19 return target;
20 };
21 return _extends.apply(this, arguments);
22 }
23
24 function _unsupportedIterableToArray(o, minLen) {
25 if (!o) return;
26 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
27 var n = Object.prototype.toString.call(o).slice(8, -1);
28 if (n === "Object" && o.constructor) n = o.constructor.name;
29 if (n === "Map" || n === "Set") return Array.from(o);
30 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
31 }
32
33 function _arrayLikeToArray(arr, len) {
34 if (len == null || len > arr.length) len = arr.length;
35
36 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
37
38 return arr2;
39 }
40
41 function _createForOfIteratorHelperLoose(o, allowArrayLike) {
42 var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
43 if (it) return (it = it.call(o)).next.bind(it);
44
45 if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
46 if (it) o = it;
47 var i = 0;
48 return function () {
49 if (i >= o.length) return {
50 done: true
51 };
52 return {
53 done: false,
54 value: o[i++]
55 };
56 };
57 }
58
59 throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
60 }
61
62 var RESTORE_ATOMS = 'h';
63 var DEV_SUBSCRIBE_STATE = 'n';
64 var DEV_GET_MOUNTED_ATOMS = 'l';
65 var DEV_GET_ATOM_STATE = 'a';
66 var DEV_GET_MOUNTED = 'm';
67
68 var atomToPrintable$1 = function atomToPrintable(atom) {
69 return atom.debugLabel || atom.toString();
70 };
71
72 var stateToPrintable = function stateToPrintable(_ref) {
73 var store = _ref[0],
74 atoms = _ref[1];
75 return Object.fromEntries(atoms.flatMap(function (atom) {
76 var _store$DEV_GET_MOUNTE, _store$DEV_GET_ATOM_S;
77
78 var mounted = (_store$DEV_GET_MOUNTE = store[DEV_GET_MOUNTED]) == null ? void 0 : _store$DEV_GET_MOUNTE.call(store, atom);
79
80 if (!mounted) {
81 return [];
82 }
83
84 var dependents = mounted.t;
85 var atomState = ((_store$DEV_GET_ATOM_S = store[DEV_GET_ATOM_STATE]) == null ? void 0 : _store$DEV_GET_ATOM_S.call(store, atom)) || {};
86 return [[atomToPrintable$1(atom), _extends({}, 'e' in atomState && {
87 error: atomState.e
88 }, 'p' in atomState && {
89 promise: atomState.p
90 }, 'v' in atomState && {
91 value: atomState.v
92 }, {
93 dependents: Array.from(dependents).map(atomToPrintable$1)
94 })]];
95 }));
96 };
97
98 var useAtomsDebugValue = function useAtomsDebugValue(options) {
99 var _options$enabled;
100
101 var enabled = (_options$enabled = options == null ? void 0 : options.enabled) != null ? _options$enabled : true;
102 var ScopeContext = jotai.SECRET_INTERNAL_getScopeContext(options == null ? void 0 : options.scope);
103
104 var _useContext = react.useContext(ScopeContext),
105 store = _useContext.s;
106
107 var _useState = react.useState([]),
108 atoms = _useState[0],
109 setAtoms = _useState[1];
110
111 react.useEffect(function () {
112 var _store$DEV_SUBSCRIBE_;
113
114 if (!enabled) {
115 return;
116 }
117
118 var callback = function callback() {
119 var _store$DEV_GET_MOUNTE2;
120
121 setAtoms(Array.from(((_store$DEV_GET_MOUNTE2 = store[DEV_GET_MOUNTED_ATOMS]) == null ? void 0 : _store$DEV_GET_MOUNTE2.call(store)) || []));
122 };
123
124 var unsubscribe = (_store$DEV_SUBSCRIBE_ = store[DEV_SUBSCRIBE_STATE]) == null ? void 0 : _store$DEV_SUBSCRIBE_.call(store, callback);
125 callback();
126 return unsubscribe;
127 }, [enabled, store]);
128 react.useDebugValue([store, atoms], stateToPrintable);
129 };
130
131 function useAtomDevtools(anAtom, options, deprecatedScope) {
132 if (typeof options === 'string' || typeof deprecatedScope !== 'undefined') {
133 console.warn('DEPRECATED [useAtomDevtools] use DevtoolOptions');
134 options = {
135 name: options,
136 scope: deprecatedScope
137 };
138 }
139
140 var _ref = options || {},
141 enabled = _ref.enabled,
142 name = _ref.name,
143 scope = _ref.scope;
144
145 var extension;
146
147 try {
148 extension = (enabled != null ? enabled : true) && window.__REDUX_DEVTOOLS_EXTENSION__;
149 } catch (_unused) {}
150
151 if (!extension) {
152 if (enabled) {
153 console.warn('Please install/enable Redux devtools extension');
154 }
155 }
156
157 var _useAtom = jotai.useAtom(anAtom, scope),
158 value = _useAtom[0],
159 setValue = _useAtom[1];
160
161 var lastValue = react.useRef(value);
162 var isTimeTraveling = react.useRef(false);
163 var devtools = react.useRef();
164 var atomName = name || anAtom.debugLabel || anAtom.toString();
165 react.useEffect(function () {
166 if (!extension) {
167 return;
168 }
169
170 var setValueIfWritable = function setValueIfWritable(value) {
171 if (typeof setValue === 'function') {
172 setValue(value);
173 return;
174 }
175
176 console.warn('[Warn] you cannot do write operations (Time-travelling, etc) in read-only atoms\n', anAtom);
177 };
178
179 devtools.current = extension.connect({
180 name: atomName
181 });
182 var unsubscribe = devtools.current.subscribe(function (message) {
183 var _message$payload3, _message$payload4;
184
185 if (message.type === 'ACTION' && message.payload) {
186 try {
187 setValueIfWritable(JSON.parse(message.payload));
188 } catch (e) {
189 console.error('please dispatch a serializable value that JSON.parse() support\n', e);
190 }
191 } else if (message.type === 'DISPATCH' && message.state) {
192 var _message$payload, _message$payload2;
193
194 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') {
195 isTimeTraveling.current = true;
196 setValueIfWritable(JSON.parse(message.state));
197 }
198 } else if (message.type === 'DISPATCH' && ((_message$payload3 = message.payload) == null ? void 0 : _message$payload3.type) === 'COMMIT') {
199 var _devtools$current;
200
201 (_devtools$current = devtools.current) == null ? void 0 : _devtools$current.init(lastValue.current);
202 } else if (message.type === 'DISPATCH' && ((_message$payload4 = message.payload) == null ? void 0 : _message$payload4.type) === 'IMPORT_STATE') {
203 var _message$payload$next;
204
205 var computedStates = ((_message$payload$next = message.payload.nextLiftedState) == null ? void 0 : _message$payload$next.computedStates) || [];
206 computedStates.forEach(function (_ref2, index) {
207 var state = _ref2.state;
208
209 if (index === 0) {
210 var _devtools$current2;
211
212 (_devtools$current2 = devtools.current) == null ? void 0 : _devtools$current2.init(state);
213 } else {
214 setValueIfWritable(state);
215 }
216 });
217 }
218 });
219 devtools.current.shouldInit = true;
220 return unsubscribe;
221 }, [anAtom, extension, atomName, setValue]);
222 react.useEffect(function () {
223 if (!devtools.current) {
224 return;
225 }
226
227 lastValue.current = value;
228
229 if (devtools.current.shouldInit) {
230 devtools.current.init(value);
231 devtools.current.shouldInit = false;
232 } else if (isTimeTraveling.current) {
233 isTimeTraveling.current = false;
234 } else {
235 devtools.current.send(atomName + " - " + new Date().toLocaleString(), value);
236 }
237 }, [anAtom, extension, atomName, value]);
238 }
239
240 var isEqualAtomsValues$1 = function isEqualAtomsValues(left, right) {
241 return left.size === right.size && Array.from(left).every(function (_ref) {
242 var left = _ref[0],
243 v = _ref[1];
244 return Object.is(right.get(left), v);
245 });
246 };
247
248 var isEqualAtomsDependents$1 = function isEqualAtomsDependents(left, right) {
249 return left.size === right.size && Array.from(left).every(function (_ref2) {
250 var a = _ref2[0],
251 dLeft = _ref2[1];
252 var dRight = right.get(a);
253 return dRight && dLeft.size === dRight.size && Array.from(dLeft).every(function (d) {
254 return dRight.has(d);
255 });
256 });
257 };
258
259 function useAtomsSnapshot(scope) {
260 var ScopeContext = jotai.SECRET_INTERNAL_getScopeContext(scope);
261 var scopeContainer = react.useContext(ScopeContext);
262 var store = scopeContainer.s;
263
264 if (!store[DEV_SUBSCRIBE_STATE]) {
265 throw new Error('useAtomsSnapshot can only be used in dev mode.');
266 }
267
268 var _useState = react.useState(function () {
269 return {
270 values: new Map(),
271 dependents: new Map()
272 };
273 }),
274 atomsSnapshot = _useState[0],
275 setAtomsSnapshot = _useState[1];
276
277 react.useEffect(function () {
278 var _store$DEV_SUBSCRIBE_;
279
280 var prevValues = new Map();
281 var prevDependents = new Map();
282 var invalidatedAtoms = new Set();
283
284 var callback = function callback() {
285 var values = new Map();
286 var dependents = new Map();
287 var hasNewInvalidatedAtoms = false;
288
289 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;) {
290 var _store$DEV_GET_MOUNTE, _store$DEV_GET_ATOM_S, _store$DEV_GET_MOUNTE2;
291
292 var atom = _step.value;
293 var atomState = (_store$DEV_GET_ATOM_S = store[DEV_GET_ATOM_STATE]) == null ? void 0 : _store$DEV_GET_ATOM_S.call(store, atom);
294
295 if (atomState) {
296 if (!atomState.y) {
297 if ('p' in atomState) {
298 return;
299 }
300
301 if (!invalidatedAtoms.has(atom)) {
302 invalidatedAtoms.add(atom);
303 hasNewInvalidatedAtoms = true;
304 }
305 }
306
307 if ('v' in atomState) {
308 values.set(atom, atomState.v);
309 }
310 }
311
312 var mounted = (_store$DEV_GET_MOUNTE2 = store[DEV_GET_MOUNTED]) == null ? void 0 : _store$DEV_GET_MOUNTE2.call(store, atom);
313
314 if (mounted) {
315 dependents.set(atom, mounted.t);
316 }
317 }
318
319 if (hasNewInvalidatedAtoms) {
320 return;
321 }
322
323 if (isEqualAtomsValues$1(prevValues, values) && isEqualAtomsDependents$1(prevDependents, dependents)) {
324 return;
325 }
326
327 prevValues = values;
328 prevDependents = dependents;
329 invalidatedAtoms.clear();
330 setAtomsSnapshot({
331 values: values,
332 dependents: dependents
333 });
334 };
335
336 var unsubscribe = (_store$DEV_SUBSCRIBE_ = store[DEV_SUBSCRIBE_STATE]) == null ? void 0 : _store$DEV_SUBSCRIBE_.call(store, callback);
337 callback();
338 return unsubscribe;
339 }, [store]);
340 return atomsSnapshot;
341 }
342
343 function useGotoAtomsSnapshot(scope) {
344 var ScopeContext = jotai.SECRET_INTERNAL_getScopeContext(scope);
345
346 var _useContext = react.useContext(ScopeContext),
347 store = _useContext.s,
348 versionedWrite = _useContext.w;
349
350 if (!store[DEV_SUBSCRIBE_STATE]) {
351 throw new Error('useGotoAtomsSnapshot can only be used in dev mode.');
352 }
353
354 return react.useCallback(function (snapshot) {
355 var restoreAtoms = function restoreAtoms(values) {
356 if (versionedWrite) {
357 versionedWrite(function (version) {
358 store[RESTORE_ATOMS](values, version);
359 });
360 } else {
361 store[RESTORE_ATOMS](values);
362 }
363 };
364
365 if (isIterable(snapshot)) {
366 {
367 console.warn('snapshot as iterable is deprecated. use an object instead.');
368 }
369
370 restoreAtoms(snapshot);
371 return;
372 }
373
374 restoreAtoms(snapshot.values);
375 }, [store, versionedWrite]);
376 }
377
378 var isIterable = function isIterable(item) {
379 return typeof item[Symbol.iterator] === 'function';
380 };
381
382 var isEqualAtomsValues = function isEqualAtomsValues(left, right) {
383 return left.size === right.size && Array.from(left).every(function (_ref) {
384 var left = _ref[0],
385 v = _ref[1];
386 return Object.is(right.get(left), v);
387 });
388 };
389
390 var isEqualAtomsDependents = function isEqualAtomsDependents(left, right) {
391 return left.size === right.size && Array.from(left).every(function (_ref2) {
392 var a = _ref2[0],
393 dLeft = _ref2[1];
394 var dRight = right.get(a);
395 return dRight && dLeft.size === dRight.size && Array.from(dLeft).every(function (d) {
396 return dRight.has(d);
397 });
398 });
399 };
400
401 var atomToPrintable = function atomToPrintable(atom) {
402 return atom.debugLabel ? atom + ":" + atom.debugLabel : "" + atom;
403 };
404
405 var getDevtoolsState = function getDevtoolsState(atomsSnapshot) {
406 var values = {};
407 atomsSnapshot.values.forEach(function (v, atom) {
408 values[atomToPrintable(atom)] = v;
409 });
410 var dependents = {};
411 atomsSnapshot.dependents.forEach(function (d, atom) {
412 dependents[atomToPrintable(atom)] = Array.from(d).map(atomToPrintable);
413 });
414 return {
415 values: values,
416 dependents: dependents
417 };
418 };
419
420 function useAtomsDevtools(name, options) {
421 if (typeof options !== 'undefined' && typeof options !== 'object') {
422 console.warn('DEPRECATED [useAtomsDevtools] use DevtoolsOptions');
423 options = {
424 scope: options
425 };
426 }
427
428 var _ref3 = options || {},
429 enabled = _ref3.enabled,
430 scope = _ref3.scope;
431
432 var ScopeContext = jotai.SECRET_INTERNAL_getScopeContext(scope);
433
434 var _useContext = react.useContext(ScopeContext),
435 store = _useContext.s,
436 versionedWrite = _useContext.w;
437
438 var extension;
439
440 try {
441 extension = (enabled != null ? enabled : true) && window.__REDUX_DEVTOOLS_EXTENSION__;
442 } catch (_unused) {}
443
444 if (!extension) {
445 if (enabled) {
446 console.warn('Please install/enable Redux devtools extension');
447 }
448 }
449
450 if (extension && !store[DEV_SUBSCRIBE_STATE]) {
451 throw new Error('useAtomsDevtools can only be used in dev mode.');
452 }
453
454 var _useState = react.useState(function () {
455 return {
456 values: new Map(),
457 dependents: new Map()
458 };
459 }),
460 atomsSnapshot = _useState[0],
461 setAtomsSnapshot = _useState[1];
462
463 react.useEffect(function () {
464 var _store$DEV_SUBSCRIBE_;
465
466 if (!extension) {
467 return;
468 }
469
470 var prevValues = new Map();
471 var prevDependents = new Map();
472 var invalidatedAtoms = new Set();
473
474 var callback = function callback() {
475 var values = new Map();
476 var dependents = new Map();
477 var hasNewInvalidatedAtoms = false;
478
479 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;) {
480 var _store$DEV_GET_MOUNTE, _store$DEV_GET_ATOM_S, _store$DEV_GET_MOUNTE2;
481
482 var atom = _step.value;
483 var atomState = (_store$DEV_GET_ATOM_S = store[DEV_GET_ATOM_STATE]) == null ? void 0 : _store$DEV_GET_ATOM_S.call(store, atom);
484
485 if (atomState) {
486 if (!atomState.y) {
487 if ('p' in atomState) {
488 return;
489 }
490
491 if (!invalidatedAtoms.has(atom)) {
492 invalidatedAtoms.add(atom);
493 hasNewInvalidatedAtoms = true;
494 }
495 }
496
497 if ('v' in atomState) {
498 values.set(atom, atomState.v);
499 }
500 }
501
502 var mounted = (_store$DEV_GET_MOUNTE2 = store[DEV_GET_MOUNTED]) == null ? void 0 : _store$DEV_GET_MOUNTE2.call(store, atom);
503
504 if (mounted) {
505 dependents.set(atom, mounted.t);
506 }
507 }
508
509 if (hasNewInvalidatedAtoms) {
510 return;
511 }
512
513 if (isEqualAtomsValues(prevValues, values) && isEqualAtomsDependents(prevDependents, dependents)) {
514 return;
515 }
516
517 prevValues = values;
518 prevDependents = dependents;
519 invalidatedAtoms.clear();
520 setAtomsSnapshot({
521 values: values,
522 dependents: dependents
523 });
524 };
525
526 var unsubscribe = (_store$DEV_SUBSCRIBE_ = store[DEV_SUBSCRIBE_STATE]) == null ? void 0 : _store$DEV_SUBSCRIBE_.call(store, callback);
527 callback();
528 return unsubscribe;
529 }, [extension, store]);
530 var goToSnapshot = react.useCallback(function (snapshot) {
531 var values = snapshot.values;
532
533 if (versionedWrite) {
534 versionedWrite(function (version) {
535 store[RESTORE_ATOMS](values, version);
536 });
537 } else {
538 store[RESTORE_ATOMS](values);
539 }
540 }, [store, versionedWrite]);
541 var isTimeTraveling = react.useRef(false);
542 var isRecording = react.useRef(true);
543 var devtools = react.useRef();
544 var snapshots = react.useRef([]);
545 react.useEffect(function () {
546 if (!extension) {
547 return;
548 }
549
550 var getSnapshotAt = function getSnapshotAt(index) {
551 if (index === void 0) {
552 index = snapshots.current.length - 1;
553 }
554
555 var snapshot = snapshots.current[index >= 0 ? index : 0];
556
557 if (!snapshot) {
558 throw new Error('snaphost index out of bounds');
559 }
560
561 return snapshot;
562 };
563
564 var connection = extension.connect({
565 name: name
566 });
567 var devtoolsUnsubscribe = connection.subscribe(function (message) {
568 var _message$payload;
569
570 switch (message.type) {
571 case 'DISPATCH':
572 switch ((_message$payload = message.payload) == null ? void 0 : _message$payload.type) {
573 case 'RESET':
574 break;
575
576 case 'COMMIT':
577 connection.init(getDevtoolsState(getSnapshotAt()));
578 snapshots.current = [];
579 break;
580
581 case 'JUMP_TO_ACTION':
582 case 'JUMP_TO_STATE':
583 isTimeTraveling.current = true;
584 goToSnapshot(getSnapshotAt(message.payload.actionId - 1));
585 break;
586
587 case 'PAUSE_RECORDING':
588 isRecording.current = !isRecording.current;
589 break;
590 }
591
592 }
593 });
594 devtools.current = connection;
595 devtools.current.shouldInit = true;
596 return devtoolsUnsubscribe;
597 }, [extension, goToSnapshot, name]);
598 react.useEffect(function () {
599 if (!devtools.current) {
600 return;
601 }
602
603 if (devtools.current.shouldInit) {
604 devtools.current.init(undefined);
605 devtools.current.shouldInit = false;
606 return;
607 }
608
609 if (isTimeTraveling.current) {
610 isTimeTraveling.current = false;
611 } else if (isRecording.current) {
612 snapshots.current.push(atomsSnapshot);
613 devtools.current.send({
614 type: "" + snapshots.current.length,
615 updatedAt: new Date().toLocaleString()
616 }, getDevtoolsState(atomsSnapshot));
617 }
618 }, [atomsSnapshot]);
619 }
620
621 exports.useAtomDevtools = useAtomDevtools;
622 exports.useAtomsDebugValue = useAtomsDebugValue;
623 exports.useAtomsDevtools = useAtomsDevtools;
624 exports.useAtomsSnapshot = useAtomsSnapshot;
625 exports.useGotoAtomsSnapshot = useGotoAtomsSnapshot;
626
627 Object.defineProperty(exports, '__esModule', { value: true });
628
629}));