addons/isl/src/debug/getInterestingAtoms.tsblame
View source
b69ab311/**
b69ab312 * Copyright (c) Meta Platforms, Inc. and affiliates.
b69ab313 *
b69ab314 * This source code is licensed under the MIT license found in the
b69ab315 * LICENSE file in the root directory of this source tree.
b69ab316 */
b69ab317
b69ab318import type {Atom} from 'jotai';
b69ab319import type {Json} from 'shared/typeUtils';
b69ab3110import type {AtomFamilyWeak} from '../jotaiUtils';
b69ab3111
b69ab3112import {SelfUpdate} from 'shared/immutableExt';
b69ab3113import {editedCommitMessages} from '../CommitInfoView/CommitInfoState';
b69ab3114import {latestSuccessorsMapAtom} from '../SuccessionTracker';
b69ab3115import {allDiffSummaries, codeReviewProvider} from '../codeReview/CodeReviewInfo';
b69ab3116import {readAtom} from '../jotaiUtils';
b69ab3117import {operationBeingPreviewed, operationList, queuedOperations} from '../operationsState';
b69ab3118import {uncommittedSelection} from '../partialSelection';
b69ab3119import {dagWithPreviews} from '../previews';
b69ab3120import {selectedCommits} from '../selection';
b69ab3121import {
b69ab3122 latestCommitsData,
b69ab3123 latestUncommittedChangesData,
b69ab3124 mergeConflicts,
b69ab3125 repositoryData,
b69ab3126 submodulesByRoot,
b69ab3127} from '../serverAPIState';
b69ab3128
b69ab3129export type UIStateSnapshot = {[key: string]: Json};
b69ab3130export type AtomsState = {[key: string]: unknown};
b69ab3131
b69ab3132type AtomOrFamily = Atom<unknown> | AtomFamilyWeak<string, Atom<unknown>>;
b69ab3133
b69ab3134function listInterestingAtoms(): Array<AtomOrFamily> {
b69ab3135 return [
b69ab3136 allDiffSummaries,
b69ab3137 codeReviewProvider,
b69ab3138 repositoryData,
b69ab3139 latestCommitsData,
b69ab3140 latestSuccessorsMapAtom,
b69ab3141 latestUncommittedChangesData,
b69ab3142 dagWithPreviews,
b69ab3143 mergeConflicts,
b69ab3144 operationBeingPreviewed,
b69ab3145 operationList,
b69ab3146 queuedOperations,
b69ab3147 selectedCommits,
b69ab3148 uncommittedSelection,
b69ab3149 // These are atomFamilies.
b69ab3150 editedCommitMessages,
b69ab3151 submodulesByRoot,
b69ab3152 ];
b69ab3153}
b69ab3154
b69ab3155/** Read all "interesting" atoms and returns a single object that contains them all. */
b69ab3156export function readInterestingAtoms(): AtomsState {
b69ab3157 return Object.fromEntries(
b69ab3158 listInterestingAtoms().map(a => [a.debugLabel ?? a.toString(), readAtomOrFamily(a)]),
b69ab3159 );
b69ab3160}
b69ab3161
b69ab3162/** Try to serialize the `state` so they can be represented in plain JSON. */
b69ab3163export function serializeAtomsState(state: AtomsState): UIStateSnapshot {
b69ab3164 const newEntries = Object.entries(state).map(([key, value]) => {
b69ab3165 return [key, serialize(value as Serializable)];
b69ab3166 });
b69ab3167 return Object.fromEntries(newEntries);
b69ab3168}
b69ab3169
b69ab3170function readAtomOrFamily(atomOrFamily: AtomOrFamily): unknown {
b69ab3171 if (typeof atomOrFamily === 'function') {
b69ab3172 // atomFamily. Read its values from weakCache.
b69ab3173 const result = new Map<string, unknown>();
b69ab3174 for (const [key, weak] of atomOrFamily.weakCache.entries()) {
b69ab3175 const value = weak.deref();
b69ab3176 result.set(key, value === undefined ? undefined : readAtom(value));
b69ab3177 }
b69ab3178 return result;
b69ab3179 } else {
b69ab3180 return readAtom(atomOrFamily);
b69ab3181 }
b69ab3182}
b69ab3183
b69ab3184type Serializable = Json | {toJSON: () => Serializable};
b69ab3185
b69ab3186function serialize(initialArg: Serializable): Json {
b69ab3187 let arg = initialArg;
b69ab3188
b69ab3189 const isObject = arg != null && typeof arg === 'object';
b69ab3190
b69ab3191 // Extract debug state provided by the object. This applies to both immutable and regular objects.
b69ab3192 // This needs to happen before unwrapping SelfUpdate.
b69ab3193 let debugState = null;
b69ab3194 if (isObject) {
b69ab3195 // If the object defines `getDebugState`. Call it to get more (easier to visualize) states.
b69ab3196 const maybeGetDebugState = (arg as {getDebugState?: () => {[key: string]: Json}}).getDebugState;
b69ab3197 if (maybeGetDebugState != null) {
b69ab3198 debugState = maybeGetDebugState.call(arg);
b69ab3199 }
b69ab31100 }
b69ab31101
b69ab31102 // Unwrap SelfUpdate types.
b69ab31103 if (arg instanceof SelfUpdate) {
b69ab31104 arg = arg.inner;
b69ab31105 }
b69ab31106
b69ab31107 // Convert known immutable types.
b69ab31108 if (arg != null && typeof arg === 'object') {
b69ab31109 const maybeToJSON = (arg as {toJSON?: () => Json}).toJSON;
b69ab31110 if (maybeToJSON !== undefined) {
b69ab31111 arg = maybeToJSON.call(arg);
b69ab31112 if (typeof arg === 'object' && debugState != null) {
b69ab31113 arg = {...debugState, ...arg};
b69ab31114 }
b69ab31115 }
b69ab31116 }
b69ab31117
b69ab31118 if (arg === undefined) {
b69ab31119 return null;
b69ab31120 }
b69ab31121
b69ab31122 if (
b69ab31123 typeof arg === 'number' ||
b69ab31124 typeof arg === 'boolean' ||
b69ab31125 typeof arg === 'string' ||
b69ab31126 arg === null
b69ab31127 ) {
b69ab31128 return arg;
b69ab31129 }
b69ab31130
b69ab31131 if (arg instanceof Map) {
b69ab31132 return Array.from(arg.entries()).map(([key, val]) => [serialize(key), serialize(val)]);
b69ab31133 } else if (arg instanceof Set) {
b69ab31134 return Array.from(arg.values()).map(serialize);
b69ab31135 } else if (arg instanceof Error) {
b69ab31136 return {message: arg.message ?? null, stack: arg.stack ?? null};
b69ab31137 } else if (arg instanceof Date) {
b69ab31138 return `Date: ${arg.valueOf()}`;
b69ab31139 } else if (Array.isArray(arg)) {
b69ab31140 return arg.map(a => serialize(a));
b69ab31141 } else if (typeof arg === 'object') {
b69ab31142 const newObj: Json = debugState ?? {};
b69ab31143 for (const [propertyName, propertyValue] of Object.entries(arg)) {
b69ab31144 // Skip functions.
b69ab31145 if (typeof propertyValue === 'function') {
b69ab31146 continue;
b69ab31147 }
b69ab31148 newObj[propertyName] = serialize(propertyValue);
b69ab31149 }
b69ab31150
b69ab31151 return newObj;
b69ab31152 }
b69ab31153
b69ab31154 // Return a dummy value instead of throw so if an item in a container is "bad",
b69ab31155 // it does not turn the whole container into an error.
b69ab31156 return `<unserializable: ${arg}>`;
b69ab31157}