UNPKG

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