From 23b43f32dda7e784adca45b8aba897c719496ecf Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Tue, 7 May 2024 07:21:32 -0700 Subject: [PATCH 01/92] Compose support dependency cleanup Summary: This diff cleans up Flipper's BUCK setup and updates the ui-inspection files to match the latest version of those files in AOSP. It also addresses a few lint errors related to minSdk level. Reviewed By: pengj Differential Revision: D56997944 fbshipit-source-id: 1964c8ef0e141a3018b1d7e1f7eadf103e239739 --- .../AbstractComposeViewDescriptor.kt | 8 ++++++++ .../descriptors/ComposeNodeDescriptor.kt | 3 +++ .../plugins/jetpackcompose/model/ComposeNode.kt | 1 + .../androidx/compose/ui/inspection/BUCK | 17 +++++++++++++++++ .../androidx/compose/ui/inspection/METADATA.bzl | 8 ++++++++ .../compose/ui/inspection/README.facebook | 2 +- .../compose/ui/inspection/inspector/BUCK | 16 ++++++++++++++++ .../inspection/inspector/LayoutInspectorTree.kt | 7 +++++-- .../ui/inspection/inspector/ParameterFactory.kt | 4 ++-- .../compose/ui/inspection/util/AnchorMap.kt | 2 +- .../androidx/compose/ui/inspection/util/BUCK | 9 +++++++++ .../ui/inspection/util/CollectionUtil.kt | 2 +- .../compose/ui/inspection/util/ThreadUtils.kt | 5 ++++- 13 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/BUCK create mode 100644 android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/METADATA.bzl create mode 100644 android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/BUCK create mode 100644 android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/BUCK diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/AbstractComposeViewDescriptor.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/AbstractComposeViewDescriptor.kt index 58bdca8d3b7..02f05bc3241 100644 --- a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/AbstractComposeViewDescriptor.kt +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/AbstractComposeViewDescriptor.kt @@ -22,6 +22,7 @@ import facebook.internal.androidx.compose.ui.inspection.inspector.InspectorNode import facebook.internal.androidx.compose.ui.inspection.inspector.LayoutInspectorTree import java.io.IOException +@RequiresApi(Build.VERSION_CODES.Q) object AbstractComposeViewDescriptor : ChainedDescriptor() { private val recompositionHandler by lazy { RecompositionHandler(DefaultArtTooling("Flipper")).apply { @@ -51,6 +52,13 @@ object AbstractComposeViewDescriptor : ChainedDescriptor() } override fun onGetChildren(node: AbstractComposeView): List { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + return listOf( + WarningMessage( + "Flipper Compose Plugin works only on devices with Android Q (API 29) and above.", + getBounds(node))) + } + val children = mutableListOf() val count = node.childCount - 1 for (i in 0..count) { diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeNodeDescriptor.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeNodeDescriptor.kt index ac6ae159cb9..65695bb77b4 100644 --- a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeNodeDescriptor.kt +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeNodeDescriptor.kt @@ -7,6 +7,8 @@ package com.facebook.flipper.plugins.jetpackcompose.descriptors +import android.os.Build +import androidx.annotation.RequiresApi import com.facebook.flipper.plugins.jetpackcompose.JetpackComposeTag import com.facebook.flipper.plugins.jetpackcompose.descriptors.ComposeNodeDescriptor.toInspectableValue import com.facebook.flipper.plugins.jetpackcompose.model.ComposeNode @@ -26,6 +28,7 @@ import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred import facebook.internal.androidx.compose.ui.inspection.inspector.NodeParameter import facebook.internal.androidx.compose.ui.inspection.inspector.ParameterType +@RequiresApi(Build.VERSION_CODES.Q) object ComposeNodeDescriptor : NodeDescriptor { private const val NAMESPACE = "ComposeNode" diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeNode.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeNode.kt index b57e34949df..2e80c5a0def 100644 --- a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeNode.kt +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeNode.kt @@ -23,6 +23,7 @@ import facebook.internal.androidx.compose.ui.inspection.inspector.ParameterKind private const val MAX_RECURSIONS = 2 private const val MAX_ITERABLE_SIZE = 5 +@RequiresApi(Build.VERSION_CODES.Q) class ComposeNode( private val parentComposeView: View, private val layoutInspectorTree: LayoutInspectorTree, diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/BUCK b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/BUCK new file mode 100644 index 00000000000..a2aea3c540a --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/BUCK @@ -0,0 +1,17 @@ +load("@fbsource//tools/build_defs/android:fb_android_library.bzl", "fb_android_library") + +fb_android_library( + name = "ui-inspection", + create_suffixed_alias = True, + dataclass_generate = { + "mode": "EXPLICIT", + }, + oncall = "flipper", + deps = [ + "//third-party/java/androidx/compose/ui/ui-android:ui-android-aar", + ], + exported_deps = [ + "//third-party/java/androidx/inspection/inspection:inspection", + "//xplat/sonar/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector:inspector", + ], +) diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/METADATA.bzl b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/METADATA.bzl new file mode 100644 index 00000000000..4b2a89d486b --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/METADATA.bzl @@ -0,0 +1,8 @@ +METADATA = { + "licenses": ["The Apache Software License, Version 2.0"], + "maintainers": ["androidx"], + "name": "ui-inspection", + "upstream_address": "https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection", + "upstream_hash": "f24ae42d863ea9710488e8f34deff4624840035a", + "version": "1.0.0", +} diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/README.facebook b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/README.facebook index 94ea6c9366b..ab69264869c 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/README.facebook +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/README.facebook @@ -1,5 +1,5 @@ This is a check-in of https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-main/compose/ui/ui-inspection/src/main/java/androidx/compose/ui/inspection -The classes are currently not exported but we rely on them for debug information. +The classes are currently not exported but we rely on them for debug information. The package name was updated in all of the files to avoid collisions with AOSP versions of these files which are used by Android Studio Layout Inspector. Tree: 80c942dfbd74be7bf1931364d0157a85a3517287 diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/BUCK b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/BUCK new file mode 100644 index 00000000000..e7e96c60f3a --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/BUCK @@ -0,0 +1,16 @@ +load("@fbsource//tools/build_defs/android:fb_android_library.bzl", "fb_android_library") + +fb_android_library( + name = "inspector", + create_suffixed_alias = True, + dataclass_generate = { + "mode": "EXPLICIT", + }, + oncall = "flipper", + deps = [ + "//fbandroid/third-party/android/androidx/compose:ui-tooling-data", + "//third-party/java/androidx/compose/ui/ui-android:ui-android-aar", + "//third-party/kotlin:kotlin-reflect", + "//xplat/sonar/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util:util", + ], +) diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt index dd69391d5b9..c096931d45f 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt @@ -16,7 +16,9 @@ package facebook.internal.androidx.compose.ui.inspection.inspector +import android.os.Build import android.view.View +import androidx.annotation.RequiresApi import androidx.annotation.VisibleForTesting import androidx.collection.LongList import androidx.collection.mutableIntObjectMapOf @@ -27,8 +29,6 @@ import androidx.compose.runtime.tooling.CompositionGroup import androidx.compose.ui.InternalComposeUiApi import androidx.compose.ui.R import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.inspection.util.AnchorMap -import androidx.compose.ui.inspection.util.NO_ANCHOR_ID import androidx.compose.ui.layout.GraphicLayerInfo import androidx.compose.ui.layout.LayoutInfo import androidx.compose.ui.node.InteroperableComposeUiNode @@ -48,6 +48,8 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntRect import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.toSize +import facebook.internal.androidx.compose.ui.inspection.util.AnchorMap +import facebook.internal.androidx.compose.ui.inspection.util.NO_ANCHOR_ID import java.util.ArrayDeque import java.util.Collections import java.util.IdentityHashMap @@ -79,6 +81,7 @@ private val unwantedCalls = /** Generator of a tree for the Layout Inspector. */ @OptIn(UiToolingDataApi::class) +@RequiresApi(Build.VERSION_CODES.Q) class LayoutInspectorTree { @Suppress("MemberVisibilityCanBePrivate") var hideSystemNodes = true var includeNodesOutsizeOfWindow = true diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ParameterFactory.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ParameterFactory.kt index 2a4408256d4..ada5306a879 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ParameterFactory.kt +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ParameterFactory.kt @@ -30,8 +30,6 @@ import androidx.compose.ui.graphics.Shadow import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.inspection.util.copy -import androidx.compose.ui.inspection.util.removeLast import androidx.compose.ui.platform.InspectableModifier import androidx.compose.ui.platform.InspectableValue import androidx.compose.ui.text.AnnotatedString @@ -48,6 +46,8 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnitType import facebook.internal.androidx.compose.ui.inspection.inspector.ParameterType.DimensionDp +import facebook.internal.androidx.compose.ui.inspection.util.copy +import facebook.internal.androidx.compose.ui.inspection.util.removeLast import java.lang.reflect.Field import java.lang.reflect.Modifier as JavaModifier import java.util.IdentityHashMap diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/AnchorMap.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/AnchorMap.kt index d0f8eabc975..247d15a01a4 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/AnchorMap.kt +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/AnchorMap.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package androidx.compose.ui.inspection.util +package facebook.internal.androidx.compose.ui.inspection.util const val NO_ANCHOR_ID = 0 diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/BUCK b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/BUCK new file mode 100644 index 00000000000..105cca8ed1a --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/BUCK @@ -0,0 +1,9 @@ +load("@fbsource//tools/build_defs/android:fb_android_library.bzl", "fb_android_library") + +fb_android_library( + name = "util", + oncall = "flipper", + deps = [ + "//third-party/java/androidx/collection/collection:collection", + ], +) diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/CollectionUtil.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/CollectionUtil.kt index e066f3185e2..d4f67498279 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/CollectionUtil.kt +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/CollectionUtil.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package androidx.compose.ui.inspection.util +package facebook.internal.androidx.compose.ui.inspection.util import androidx.collection.IntList import androidx.collection.MutableIntList diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/ThreadUtils.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/ThreadUtils.kt index d4804b28704..59e2931c219 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/ThreadUtils.kt +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/ThreadUtils.kt @@ -14,13 +14,16 @@ * limitations under the License. */ -package androidx.compose.ui.inspection.util +package facebook.internal.androidx.compose.ui.inspection.util +import android.os.Build import android.os.Handler import android.os.Looper +import androidx.annotation.RequiresApi import java.util.concurrent.CompletableFuture import java.util.concurrent.Future +@RequiresApi(Build.VERSION_CODES.P) object ThreadUtils { fun assertOnMainThread() { if (!Looper.getMainLooper().isCurrentThread) { From cee45141dc024654d8e68c9b4e46c0cea1c59a6c Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Tue, 7 May 2024 07:21:32 -0700 Subject: [PATCH 02/92] Update gradle dependency versions Summary: This diff updates Kotlin and Compose dependencies in Flipper so that they match the ones used in BUCK. Reviewed By: hick209 Differential Revision: D56997948 fbshipit-source-id: 58627a8a4553400ad56a1e88d584e40d37dbf570 --- gradle.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle.properties b/gradle.properties index 633460a709e..81e28bb035a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,12 +21,12 @@ LITHO_VERSION=0.49.0 FRESCO_VERSION=3.1.3 ANDROIDX_VERSION=1.3.0 ANDROIDX_ACTIVITY_VERSION=1.8.2 -KOTLIN_VERSION=1.9.10 +KOTLIN_VERSION=1.9.23 FBJNI_VERSION=0.3.0 SOLOADER_VERSION=0.10.4 -COMPOSE_VERSION=1.5.4 +COMPOSE_VERSION=1.6.7 COMPOSE_MATERIAL3_VERSION=1.1.2 -COMPOSE_COMPILER_VERSION=1.5.3 +COMPOSE_COMPILER_VERSION=1.5.13 # Gradle internals org.gradle.internal.repository.max.retries=10 org.gradle.internal.repository.initial.backoff=1250 From e8669eadf6dd88afbdbdd15092a033b210168014 Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Tue, 7 May 2024 07:21:32 -0700 Subject: [PATCH 03/92] Update PackageHashes to latest version Summary: As per title Reviewed By: hick209 Differential Revision: D56997943 fbshipit-source-id: 9ff58d70b25cd6ed97e5abaacc70f45c6c6790a1 --- .../compose/ui/inspection/inspector/PackageHashes.kt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/PackageHashes.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/PackageHashes.kt index d79ed81fff2..cdbfae1c856 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/PackageHashes.kt +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/PackageHashes.kt @@ -35,22 +35,26 @@ val systemPackages = packageNameHash("androidx.compose.foundation.lazy.staggeredgrid"), packageNameHash("androidx.compose.foundation.pager"), packageNameHash("androidx.compose.foundation.text"), + packageNameHash("androidx.compose.foundation.text.input"), packageNameHash("androidx.compose.foundation.text.selection"), - packageNameHash("androidx.compose.foundation.text2"), - packageNameHash("androidx.compose.foundation.text2.input"), packageNameHash("androidx.compose.foundation.window"), packageNameHash("androidx.compose.material"), packageNameHash("androidx.compose.material.internal"), + packageNameHash("androidx.compose.material.navigation"), packageNameHash("androidx.compose.material.pullrefresh"), packageNameHash("androidx.compose.material.ripple"), packageNameHash("androidx.compose.material3"), packageNameHash("androidx.compose.material3.adaptive"), - packageNameHash("androidx.compose.material3.adaptive.navigation.suite"), + packageNameHash("androidx.compose.material3.adaptive.layout"), + packageNameHash("androidx.compose.material3.adaptive.navigation"), + packageNameHash("androidx.compose.material3.adaptive.navigationsuite"), + packageNameHash("androidx.compose.material3.carousel"), packageNameHash("androidx.compose.material3.common"), packageNameHash("androidx.compose.material3.internal"), packageNameHash("androidx.compose.material3.pulltorefresh"), packageNameHash("androidx.compose.material3.windowsizeclass"), packageNameHash("androidx.compose.runtime"), + packageNameHash("androidx.compose.runtime.internal"), packageNameHash("androidx.compose.runtime.livedata"), packageNameHash("androidx.compose.runtime.mock"), packageNameHash("androidx.compose.runtime.reflect"), @@ -59,6 +63,8 @@ val systemPackages = packageNameHash("androidx.compose.runtime.saveable"), packageNameHash("androidx.compose.ui"), packageNameHash("androidx.compose.ui.awt"), + packageNameHash("androidx.compose.ui.draw"), + packageNameHash("androidx.compose.ui.graphics"), packageNameHash("androidx.compose.ui.graphics.benchmark"), packageNameHash("androidx.compose.ui.graphics.vector"), packageNameHash("androidx.compose.ui.layout"), From c6facafa76612785a89cf2f68a90ef8c9c56cfbe Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Tue, 7 May 2024 07:21:32 -0700 Subject: [PATCH 04/92] Remove unused ThreadUtils file Summary: This file wasn't used anywhere. Reviewed By: hick209 Differential Revision: D56997946 fbshipit-source-id: 9209f3276ddefded54adfc3f776d95dfc359e9ab --- .../compose/ui/inspection/util/ThreadUtils.kt | 55 ------------------- 1 file changed, 55 deletions(-) delete mode 100644 android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/ThreadUtils.kt diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/ThreadUtils.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/ThreadUtils.kt deleted file mode 100644 index 59e2931c219..00000000000 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/ThreadUtils.kt +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package facebook.internal.androidx.compose.ui.inspection.util - -import android.os.Build -import android.os.Handler -import android.os.Looper -import androidx.annotation.RequiresApi -import java.util.concurrent.CompletableFuture -import java.util.concurrent.Future - -@RequiresApi(Build.VERSION_CODES.P) -object ThreadUtils { - fun assertOnMainThread() { - if (!Looper.getMainLooper().isCurrentThread) { - error("This work is required on the main thread") - } - } - - fun assertOffMainThread() { - if (Looper.getMainLooper().isCurrentThread) { - error("This work is required off the main thread") - } - } - - /** - * Run some logic on the main thread, returning a future that will contain any data computed by - * and returned from the block. - * - * If this method is called from the main thread, it will run immediately. - */ - fun runOnMainThread(block: () -> T): Future { - return if (!Looper.getMainLooper().isCurrentThread) { - val future = CompletableFuture() - Handler.createAsync(Looper.getMainLooper()).post { future.complete(block()) } - future - } else { - CompletableFuture.completedFuture(block()) - } - } -} From a94144391754e90b930b68da0e95beb8c546114a Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Tue, 7 May 2024 07:21:32 -0700 Subject: [PATCH 05/92] Remove reflective parameter inspection Summary: This diff improves Flipper Compose plugin performance by removing the usage of Kotlin reflection in code that parses Composable function's parameters. We're losing some information about custom types used as Composable parameters, but the Compose plugin is now usable even on big surfaces such as Threads Feed. Reviewed By: pengj Differential Revision: D56997942 fbshipit-source-id: 8eaf3f7089bc0291e5e331e75e8c7aeabe0a0412 --- .../inspector/LayoutInspectorTree.kt | 34 +- ...y.kt => ReflectionFreeParameterFactory.kt} | 459 +----------------- .../inspection/inspector/ReflectionScope.kt | 215 -------- 3 files changed, 16 insertions(+), 692 deletions(-) rename android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/{ParameterFactory.kt => ReflectionFreeParameterFactory.kt} (57%) delete mode 100644 android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionScope.kt diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt index c096931d45f..48eca52c231 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt @@ -89,7 +89,7 @@ class LayoutInspectorTree { private var foundNode: InspectorNode? = null private var windowSize = emptySize private val inlineClassConverter = InlineClassConverter() - private val parameterFactory = ParameterFactory(inlineClassConverter) + private val parameterFactory = ReflectionFreeParameterFactory() private val cache = ArrayDeque() private var generatedId = -1L private val subCompositions = SubCompositionRoots() @@ -185,38 +185,6 @@ class LayoutInspectorTree { } } - /** - * Converts a part of the [RawParameter] identified by [reference] into a displayable parameter. - * If the parameter is some sort of a collection then [startIndex] and [maxElements] describes the - * scope of the data returned. - */ - fun expandParameter( - rootId: Long, - node: InspectorNode, - reference: NodeParameterReference, - startIndex: Int, - maxElements: Int, - maxRecursions: Int, - maxInitialIterableSize: Int - ): NodeParameter? { - val parameters = node.parametersByKind(reference.kind) - if (reference.parameterIndex !in parameters.indices) { - return null - } - val parameter = parameters[reference.parameterIndex] - return parameterFactory.expand( - rootId, - node.id, - node.anchorId, - parameter.name, - parameter.value, - reference, - startIndex, - maxElements, - maxRecursions, - maxInitialIterableSize) - } - /** Reset any state accumulated between windows. */ @Suppress("unused") fun resetAccumulativeState() { diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ParameterFactory.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionFreeParameterFactory.kt similarity index 57% rename from android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ParameterFactory.kt rename to android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionFreeParameterFactory.kt index ada5306a879..a996d8b5fe9 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ParameterFactory.kt +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionFreeParameterFactory.kt @@ -16,17 +16,13 @@ package facebook.internal.androidx.compose.ui.inspection.inspector -import android.util.Log import android.view.View import androidx.collection.mutableIntListOf import androidx.collection.mutableLongObjectMapOf import androidx.compose.runtime.internal.ComposableLambda -import androidx.compose.ui.AbsoluteAlignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.RectangleShape -import androidx.compose.ui.graphics.Shadow import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.vector.ImageVector @@ -40,7 +36,6 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.ResourceFont import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.style.BaselineShift -import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.TextUnit @@ -48,70 +43,24 @@ import androidx.compose.ui.unit.TextUnitType import facebook.internal.androidx.compose.ui.inspection.inspector.ParameterType.DimensionDp import facebook.internal.androidx.compose.ui.inspection.util.copy import facebook.internal.androidx.compose.ui.inspection.util.removeLast -import java.lang.reflect.Field -import java.lang.reflect.Modifier as JavaModifier import java.util.IdentityHashMap import kotlin.jvm.internal.FunctionReference import kotlin.jvm.internal.Lambda import kotlin.math.abs -import kotlin.reflect.KClass -import kotlin.reflect.KProperty -import kotlin.reflect.KProperty1 -import kotlin.reflect.full.allSuperclasses -import kotlin.reflect.full.declaredMemberProperties -import kotlin.reflect.jvm.isAccessible -import kotlin.reflect.jvm.javaField -import kotlin.reflect.jvm.javaGetter - -private val reflectionScope: ReflectionScope = ReflectionScope() /** * Factory of [NodeParameter]s. * * Each parameter value is converted to a user readable value. + * + * Note: This file has been modified so that it doesn't rely on Kotlin reflection API. */ -internal class ParameterFactory(private val inlineClassConverter: InlineClassConverter) { - /** A map from known values to a user readable string representation. */ - private val valueLookup = mutableMapOf() - - /** The classes we have loaded constants from. */ - private val valuesLoaded = mutableSetOf>() +internal class ReflectionFreeParameterFactory { - /** - * Do not load constant names from instances of these classes. We prefer showing the raw values of - * Color and Dimensions. - */ - private val ignoredClasses = listOf(Color::class.java, Dp::class.java) private var creatorCache: ParameterCreator? = null - /** - * Do not decompose instances or lookup constants from these package prefixes - * - * The following instances are known to contain self recursion: - * - kotlinx.coroutines.flow.StateFlowImpl - * - androidx.compose.ui.node.LayoutNode - */ - private val ignoredPackagePrefixes = - listOf("android.", "java.", "javax.", "kotlinx.", "androidx.compose.ui.node.") - var density = Density(1.0f) - init { - val textDecorationCombination = - TextDecoration.combine(listOf(TextDecoration.LineThrough, TextDecoration.Underline)) - valueLookup[textDecorationCombination] = "LineThrough+Underline" - valueLookup[Color.Unspecified] = "Unspecified" - valueLookup[RectangleShape] = "RectangleShape" - valuesLoaded.add(Enum::class.java) - valuesLoaded.add(Any::class.java) - - // AbsoluteAlignment is not found from an instance of BiasAbsoluteAlignment, - // because Alignment has no file level class. - reflectionScope.withReflectiveAccess { - loadConstantsFromEnclosedClasses(AbsoluteAlignment::class.java) - } - } - /** * Create a [NodeParameter] from the specified parameter [name] and [value]. * @@ -131,63 +80,16 @@ internal class ParameterFactory(private val inlineClassConverter: InlineClassCon ): NodeParameter { val creator = creatorCache ?: ParameterCreator() try { - return reflectionScope.withReflectiveAccess { - creator.create( - rootId, - nodeId, - anchorId, - name, - value, - kind, - parameterIndex, - maxRecursions, - maxInitialIterableSize) - } - } finally { - creatorCache = creator - } - } - - /** - * Create/expand the [NodeParameter] specified by [reference]. - * - * @param rootId is the root id of the specified [nodeId]. - * @param nodeId is the [InspectorNode.id] of the node the parameter belongs to. - * @param anchorId is the [InspectorNode.anchorId] of the node the parameter belongs to. - * @param name is the name of the [reference].parameterIndex'th parameter of the node. - * @param value is the value of the [reference].parameterIndex'th parameter of the node. - * @param startIndex is the index of the 1st wanted element of a List/Array. - * @param maxElements is the max number of elements wanted from a List/Array. - * @param maxRecursions is the max recursion into composite types starting from reference. - * @param maxInitialIterableSize is the max number of elements wanted in new List/Array values. - */ - fun expand( - rootId: Long, - nodeId: Long, - anchorId: Int, - name: String, - value: Any?, - reference: NodeParameterReference, - startIndex: Int, - maxElements: Int, - maxRecursions: Int, - maxInitialIterableSize: Int - ): NodeParameter? { - val creator = creatorCache ?: ParameterCreator() - try { - return reflectionScope.withReflectiveAccess { - creator.expand( - rootId, - nodeId, - anchorId, - name, - value, - reference, - startIndex, - maxElements, - maxRecursions, - maxInitialIterableSize) - } + return creator.create( + rootId, + nodeId, + anchorId, + name, + value, + kind, + parameterIndex, + maxRecursions, + maxInitialIterableSize) } finally { creatorCache = creator } @@ -198,128 +100,6 @@ internal class ParameterFactory(private val inlineClassConverter: InlineClassCon creator.clearReferenceCache() } - private fun loadConstantsFrom(javaClass: Class<*>) { - if (valuesLoaded.contains(javaClass) || - ignoredPackagePrefixes.any { javaClass.name.startsWith(it) }) { - return - } - val related = generateSequence(javaClass) { it.superclass }.plus(javaClass.interfaces) - related.forEach { aClass -> - val topClass = generateSequence(aClass) { safeEnclosingClass(it) }.last() - loadConstantsFromEnclosedClasses(topClass) - findPackageLevelClass(topClass)?.let { loadConstantsFromStaticFinal(it) } - } - } - - private fun safeEnclosingClass(klass: Class<*>): Class<*>? = - try { - klass.enclosingClass - } catch (_: Error) { - // Exceptions seen on API 23... - null - } - - private fun findPackageLevelClass(javaClass: Class<*>): Class<*>? = - try { - // Note: This doesn't work when @file.JvmName is specified - Class.forName("${javaClass.name}Kt") - } catch (ex: Throwable) { - null - } - - private fun loadConstantsFromEnclosedClasses(javaClass: Class<*>) { - if (valuesLoaded.contains(javaClass)) { - return - } - loadConstantsFromObjectInstance(javaClass.kotlin) - loadConstantsFromStaticFinal(javaClass) - valuesLoaded.add(javaClass) - javaClass.declaredClasses.forEach { loadConstantsFromEnclosedClasses(it) } - } - - /** - * Load all constants from companion objects and singletons - * - * Exclude: primary types and types of ignoredClasses, open and lateinit vals. - */ - private fun loadConstantsFromObjectInstance(kClass: KClass<*>) { - try { - val instance = kClass.objectInstance ?: return - kClass.declaredMemberProperties - .asSequence() - .filter { it.isFinal && !it.isLateinit } - .mapNotNull { constantValueOf(it, instance)?.let { key -> Pair(key, it.name) } } - .filter { !ignoredValue(it.first) } - .toMap(valueLookup) - } catch (_: Throwable) { - // KT-16479 : kotlin reflection does currently not support packages and files. - // We load top level values using Java reflection instead. - // Ignore other reflection errors as well - } - } - - /** - * Load all constants from top level values from Java. - * - * Exclude: primary types and types of ignoredClasses. Since this is Java, inline types will also - * (unfortunately) be excluded. - */ - private fun loadConstantsFromStaticFinal(javaClass: Class<*>) { - try { - javaClass.declaredMethods - .asSequence() - .filter { - it.returnType != Void.TYPE && - JavaModifier.isStatic(it.modifiers) && - JavaModifier.isFinal(it.modifiers) && - !it.returnType.isPrimitive && - it.parameterTypes.isEmpty() && - it.name.startsWith("get") - } - .mapNotNull { javaClass.getDeclaredField(it.name.substring(3)) } - .mapNotNull { constantValueOf(it)?.let { key -> Pair(key, it.name) } } - .filter { !ignoredValue(it.first) } - .toMap(valueLookup) - } catch (_: ReflectiveOperationException) { - // ignore reflection errors - } catch (_: NoClassDefFoundError) { - // ignore missing classes on lower level SDKs - } - } - - private fun constantValueOf(field: Field?): Any? = - try { - field?.isAccessible = true - field?.get(null) - } catch (_: ReflectiveOperationException) { - // ignore reflection errors - null - } - - private fun constantValueOf(property: KProperty1, instance: Any): Any? = - try { - val field = property.javaField - field?.isAccessible = true - inlineClassConverter.castParameterValue(inlineResultClass(property), field?.get(instance)) - } catch (_: ReflectiveOperationException) { - // ignore reflection errors - null - } - - private fun inlineResultClass(property: KProperty1): String? { - // The Java getter name will be mangled if it contains parameters of an inline class. - // The mangled part starts with a '-'. - if (property.javaGetter?.name?.contains('-') == true) { - return property.returnType.toString() - } - return null - } - - private fun ignoredValue(value: Any?): Boolean = - value == null || - ignoredClasses.any { ignored -> ignored.isInstance(value) } || - value::class.java.isPrimitive - /** Convenience class for building [NodeParameter]s. */ private inner class ParameterCreator { private var rootId = 0L @@ -355,46 +135,6 @@ internal class ParameterFactory(private val inlineClassConverter: InlineClassCon setup() } - fun expand( - rootId: Long, - nodeId: Long, - anchorId: Int, - name: String, - value: Any?, - reference: NodeParameterReference, - startIndex: Int, - maxElements: Int, - maxRecursions: Int, - maxInitialIterableSize: Int - ): NodeParameter? { - setup( - rootId, - nodeId, - anchorId, - reference.kind, - reference.parameterIndex, - maxRecursions, - maxInitialIterableSize) - var parent: Pair? = null - var new = Pair(name, value) - reference.indices.forEach { index -> - parent = new - new = find(new.first, new.second, index) ?: return null - } - recursions = 0 - valueIndex.addAll(reference.indices) - val parameter = - if (startIndex == 0) { - create(new.first, new.second, parent?.second) - } else { - createFromCompositeValue(new.first, new.second, parent?.second, startIndex, maxElements) - } - if (parameter == null && reference.indices.isEmpty()) { - return createEmptyParameter(name) - } - return parameter - } - fun clearReferenceCache() { rootValueIndexCache.clear() } @@ -448,9 +188,7 @@ internal class ParameterFactory(private val inlineClassConverter: InlineClassCon if (value == null) { return null } - createFromConstant(name, value)?.let { - return it - } + return when (value) { is AnnotatedString -> NodeParameter(name, ParameterType.String, value.text) is BaselineShift -> createFromBaselineShift(name, value) @@ -496,25 +234,8 @@ internal class ParameterFactory(private val inlineClassConverter: InlineClassCon createFromSequence(name, value, value.asSequence(), startIndex, maxElements) value.javaClass.isArray -> createFromArray(name, value, startIndex, maxElements) value is Offset -> createFromOffset(name, value) - value is Shadow -> createFromShadow(name, value) value is TextStyle -> createFromTextStyle(name, value) - else -> createFromKotlinReflection(name, value) - } - - private fun find(name: String, value: Any?, index: Int): Pair? = - when { - value == null -> null - value is Modifier -> findFromModifier(name, value, index) - value is InspectableValue -> findFromInspectableValue(value, index) - value is Sequence<*> -> findFromSequence(value, index) - value is Map<*, *> -> findFromSequence(value.asSequence(), index) - value is Map.Entry<*, *> -> findFromMapEntry(value, index) - value is Iterable<*> -> findFromSequence(value.asSequence(), index) - value.javaClass.isArray -> findFromArray(value, index) - value is Offset -> findFromOffset(value, index) - value is Shadow -> findFromShadow(value, index) - value is TextStyle -> findFromTextStyle(value, index) - else -> findFromKotlinReflection(value, index) + else -> NodeParameter(name, ParameterType.String, value.toString()) } private fun createRecursively( @@ -628,11 +349,6 @@ internal class ParameterFactory(private val inlineClassConverter: InlineClassCon return createFromSequence(name, value, sequence, startIndex, maxElements) } - private fun findFromArray(value: Any, index: Int): Pair? { - val sequence = arrayToSequence(value) ?: return null - return findFromSequence(sequence, index) - } - private fun arrayToSequence(value: Any): Sequence<*>? = when (value) { is Array<*> -> value.asSequence() @@ -666,11 +382,6 @@ internal class ParameterFactory(private val inlineClassConverter: InlineClassCon null } - private fun createFromConstant(name: String, value: Any): NodeParameter? { - loadConstantsFrom(value.javaClass) - return valueLookup[value]?.let { NodeParameter(name, ParameterType.String, it) } - } - // For now: select ResourceFontFont closest to W400 and Normal, and return the resId private fun createFromFontListFamily(name: String, value: FontListFontFamily): NodeParameter? = findBestResourceFont(value)?.let { NodeParameter(name, ParameterType.Resource, it.resId) } @@ -678,64 +389,6 @@ internal class ParameterFactory(private val inlineClassConverter: InlineClassCon private fun createFromFunctionReference(name: String, value: FunctionReference): NodeParameter = NodeParameter(name, ParameterType.FunctionReference, arrayOf(value, value.name)) - private fun createFromKotlinReflection(name: String, value: Any): NodeParameter? { - val simpleName = value::class.simpleName - val properties = lookup(value) ?: return null - val parameter = NodeParameter(name, ParameterType.String, simpleName) - return when { - properties.isEmpty() -> parameter - !shouldRecurseDeeper() -> parameter.withChildReference(value) - else -> { - val elements = parameter.store(value).elements - properties.values.mapIndexedNotNullTo(elements) { index, part -> - createRecursively(part.name, valueOf(part, value), value, index) - } - parameter.removeIfEmpty(value) - } - } - } - - private fun findFromKotlinReflection(value: Any, index: Int): Pair? { - val properties = lookup(value)?.entries?.iterator()?.asSequence() ?: return null - val element = properties.elementAtOrNull(index)?.value ?: return null - return Pair(element.name, valueOf(element, value)) - } - - private fun lookup(value: Any): Map>? { - val kClass = value::class - val simpleName = kClass.simpleName - val qualifiedName = kClass.qualifiedName - if (simpleName == null || - qualifiedName == null || - ignoredPackagePrefixes.any { qualifiedName.startsWith(it) }) { - // Exit without creating a parameter for: - // - internal synthetic classes - // - certain android packages - return null - } - return try { - sequenceOf(kClass) - .plus(kClass.allSuperclasses.asSequence()) - .flatMap { it.declaredMemberProperties.asSequence() } - .associateBy { it.name } - } catch (ex: Throwable) { - Log.w("Compose", "Could not decompose ${kClass.simpleName}", ex) - null - } - } - - private fun valueOf(property: KProperty<*>, instance: Any): Any? = - try { - property.isAccessible = true - // Bug in kotlin reflection API: if the type is a nullable inline type with a null - // value, we get an IllegalArgumentException in this line: - property.getter.call(instance) - } catch (ex: Throwable) { - // TODO: Remove this warning since this is expected with nullable inline types - Log.w("Compose", "Could not get value of ${property.name}") - null - } - private fun createFromInspectableValue(name: String, value: InspectableValue): NodeParameter { val tempValue = value.valueOverride ?: "" val parameterName = name.ifEmpty { value.nameFallback } ?: "element" @@ -753,15 +406,6 @@ internal class ParameterFactory(private val inlineClassConverter: InlineClassCon return parameter.removeIfEmpty(value) } - private fun findFromInspectableValue(value: InspectableValue, index: Int): Pair? { - val elements = value.inspectableElements.toList() - if (index !in elements.indices) { - return null - } - val element = elements[index] - return Pair(element.name, element.value) - } - private fun createFromMapEntry( name: String, entry: Map.Entry<*, *>, @@ -778,13 +422,6 @@ internal class ParameterFactory(private val inlineClassConverter: InlineClassCon } } - private fun findFromMapEntry(entry: Map.Entry<*, *>, index: Int): Pair? = - when (index) { - 0 -> Pair("key", entry.key) - 1 -> Pair("value", entry.value) - else -> null - } - private fun createFromSequence( name: String, value: Any, @@ -815,11 +452,6 @@ internal class ParameterFactory(private val inlineClassConverter: InlineClassCon } } - private fun findFromSequence(value: Sequence<*>, index: Int): Pair? { - val element = value.elementAtOrNull(index) ?: return null - return Pair("[$index]", element) - } - private fun sequenceName(value: Any): String = when (value) { is Array<*> -> "Array[${value.size}]" @@ -868,16 +500,6 @@ internal class ParameterFactory(private val inlineClassConverter: InlineClassCon return collector.modifiers } - private fun findFromModifier(name: String, value: Modifier, index: Int): Pair? = - when { - name.isNotEmpty() -> { - val modifiers = unwrap(value) - if (index in modifiers.indices) Pair("", modifiers[index]) else null - } - value is InspectableValue -> findFromInspectableValue(value, index) - else -> null - } - private fun createFromOffset(name: String, value: Offset): NodeParameter { val parameter = NodeParameter(name, ParameterType.String, Offset::class.java.simpleName) val elements = parameter.elements @@ -888,35 +510,6 @@ internal class ParameterFactory(private val inlineClassConverter: InlineClassCon return parameter } - private fun findFromOffset(value: Offset, index: Int): Pair? = - when (index) { - 0 -> Pair("x", with(density) { value.x.toDp() }) - 1 -> Pair("y", with(density) { value.y.toDp() }) - else -> null - } - - // Special handling of blurRadius: convert to dp: - private fun createFromShadow(name: String, value: Shadow): NodeParameter? { - val parameter = createFromKotlinReflection(name, value) ?: return null - val elements = parameter.elements - val index = elements.indexOfFirst { it.name == "blurRadius" } - if (index >= 0) { - val existing = elements[index] - val blurRadius = with(density) { value.blurRadius.toDp().value } - elements[index] = NodeParameter("blurRadius", DimensionDp, blurRadius) - elements[index].index = existing.index - } - return parameter - } - - private fun findFromShadow(value: Shadow, index: Int): Pair? { - val result = findFromKotlinReflection(value, index) - if (result == null || result.first != "blurRadius") { - return result - } - return Pair("blurRadius", with(density) { value.blurRadius.toDp() }) - } - // Temporary handling of TextStyle: remove when TextStyle implements InspectableValue // Hide: paragraphStyle, spanStyle, platformStyle, lineHeightStyle private fun createFromTextStyle(name: String, value: TextStyle): NodeParameter? { @@ -944,28 +537,6 @@ internal class ParameterFactory(private val inlineClassConverter: InlineClassCon return parameter } - private fun findFromTextStyle(value: TextStyle, index: Int): Pair? = - when (index) { - 0 -> Pair("color", value.color) - 1 -> Pair("fontSize", value.fontSize) - 2 -> Pair("fontWeight", value.fontWeight) - 3 -> Pair("fontStyle", value.fontStyle) - 4 -> Pair("fontSynthesis", value.fontSynthesis) - 5 -> Pair("fontFamily", value.fontFamily) - 6 -> Pair("fontFeatureSettings", value.fontFeatureSettings) - 7 -> Pair("letterSpacing", value.letterSpacing) - 8 -> Pair("baselineShift", value.baselineShift) - 9 -> Pair("textGeometricTransform", value.textGeometricTransform) - 10 -> Pair("localeList", value.localeList) - 11 -> Pair("background", value.background) - 12 -> Pair("textDecoration", value.textDecoration) - 13 -> Pair("shadow", value.shadow) - 14 -> Pair("textDirection", value.textDirection) - 15 -> Pair("lineHeight", value.lineHeight) - 16 -> Pair("textIndent", value.textIndent) - else -> null - } - @Suppress("DEPRECATION") private fun createFromTextUnit(name: String, value: TextUnit): NodeParameter = when (value.type) { diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionScope.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionScope.kt deleted file mode 100644 index 8deae6a1b32..00000000000 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionScope.kt +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package facebook.internal.androidx.compose.ui.inspection.inspector - -import android.annotation.SuppressLint -import java.lang.reflect.Field -import java.lang.reflect.Modifier -import kotlin.jvm.internal.FunctionBase -import kotlin.jvm.internal.FunctionReference -import kotlin.jvm.internal.Lambda -import kotlin.jvm.internal.MutablePropertyReference0 -import kotlin.jvm.internal.MutablePropertyReference1 -import kotlin.jvm.internal.MutablePropertyReference2 -import kotlin.jvm.internal.PropertyReference0 -import kotlin.jvm.internal.PropertyReference1 -import kotlin.jvm.internal.PropertyReference2 -import kotlin.jvm.internal.Reflection -import kotlin.jvm.internal.ReflectionFactory -import kotlin.reflect.KClass -import kotlin.reflect.KClassifier -import kotlin.reflect.KDeclarationContainer -import kotlin.reflect.KFunction -import kotlin.reflect.KMutableProperty0 -import kotlin.reflect.KMutableProperty1 -import kotlin.reflect.KMutableProperty2 -import kotlin.reflect.KProperty0 -import kotlin.reflect.KProperty1 -import kotlin.reflect.KProperty2 -import kotlin.reflect.KType -import kotlin.reflect.KTypeParameter -import kotlin.reflect.KTypeProjection -import kotlin.reflect.KVariance -import kotlin.reflect.jvm.internal.ReflectionFactoryImpl - -/** - * Scope that allows to use jarjar-ed kotlin-reflect artifact that is shipped with inspector itself. - * - * Issue with kotlin-reflect. Many of reflective calls such as "foo::class" rely on static functions - * defined in kotlin-stdlib's Reflection.java that delegate to ReflectionFactory. In order to - * initialize that factory kotlin-stdlib statically detects presence or absence of kotlin-reflect in - * classloader and chooses a factory accordingly. If there is no kotlin-reflect, very limited - * version of ReflectionFactory is used. - * - * It is an issue for inspectors because they could be loaded after that factory is initialised, and - * even if they are loaded before, they live in a separate child classloader, thus kotlin-reflect in - * inspector wouldn't exist for kotlin-stdlib in app. - * - * First step to avoid the issue is using ReflectionFactoryImpl that is bundled with inspector. Code - * for that would be fairly simple, for example instead of directly calling - * `kClass.declaredMemberProperties`, correct instance of kClass should be obtained from factory: - * `factory.getOrCreateKotlinClass(kClass.java).declaredMemberProperties`. - * - * That would work if code that works with correct KClass full implementation would never try to - * access a default factory installed in Reflection.java. Unfortunately it is not true, it - * eventually calls `CallableReference.getOwner()` in stdlib that uses default factory. - * - * As a result we have to replace the factory in Reflection.java. To avoid issues with user's code - * factory that we setup is smart, by default it simply delegates to a factory that was previously - * installed. Only within `reflectionScope.withReflectiveAccess{ }` factory from kotlin-reflect is - * used. - */ -@SuppressLint("BanUncheckedReflection") -class ReflectionScope { - - companion object { - init { - allowHiddenApi() - } - } - - private val scopedReflectionFactory = installScopedReflectionFactory() - - /** Runs `block` with access to kotlin-reflect. */ - fun withReflectiveAccess(block: () -> T): T { - return scopedReflectionFactory.withMainFactory(block) - } - - private fun installScopedReflectionFactory(): ScopedReflectionFactory { - val factoryField = Reflection::class.java.getDeclaredField("factory") - factoryField.isAccessible = true - val original: ReflectionFactory = factoryField.get(null) as ReflectionFactory - val modifiersField: Field = Field::class.java.getDeclaredField("accessFlags") - modifiersField.isAccessible = true - // make field non-final 😅 b/179685774 https://youtrack.jetbrains.com/issue/KT-44795 - modifiersField.setInt(factoryField, factoryField.modifiers and Modifier.FINAL.inv()) - val scopedReflectionFactory = ScopedReflectionFactory(original) - factoryField.set(null, scopedReflectionFactory) - return scopedReflectionFactory - } -} - -@SuppressLint("BanUncheckedReflection") -private fun allowHiddenApi() { - try { - val vmDebug = Class.forName("dalvik.system.VMDebug") - val allowHiddenApiReflectionFrom = - vmDebug.getDeclaredMethod("allowHiddenApiReflectionFrom", Class::class.java) - allowHiddenApiReflectionFrom.invoke(null, ReflectionScope::class.java) - } catch (e: Throwable) { - // ignore failure, let's try to proceed without it - } -} - -private class ScopedReflectionFactory( - private val original: ReflectionFactory, -) : ReflectionFactory() { - private val mainFactory = ReflectionFactoryImpl() - private val threadLocalFactory = ThreadLocal() - - fun withMainFactory(block: () -> T): T { - threadLocalFactory.set(mainFactory) - try { - return block() - } finally { - threadLocalFactory.set(null) - } - } - - val factory: ReflectionFactory - get() = threadLocalFactory.get() ?: original - - override fun createKotlinClass(javaClass: Class<*>?): KClass<*> { - return factory.createKotlinClass(javaClass) - } - - override fun createKotlinClass(javaClass: Class<*>?, internalName: String?): KClass<*> { - return factory.createKotlinClass(javaClass, internalName) - } - - override fun getOrCreateKotlinPackage( - javaClass: Class<*>?, - moduleName: String? - ): KDeclarationContainer { - return factory.getOrCreateKotlinPackage(javaClass, moduleName) - } - - override fun getOrCreateKotlinClass(javaClass: Class<*>?): KClass<*> { - return factory.getOrCreateKotlinClass(javaClass) - } - - override fun getOrCreateKotlinClass(javaClass: Class<*>?, internalName: String?): KClass<*> { - return factory.getOrCreateKotlinClass(javaClass, internalName) - } - - override fun renderLambdaToString(lambda: Lambda<*>?): String { - return factory.renderLambdaToString(lambda) - } - - override fun renderLambdaToString(lambda: FunctionBase<*>?): String { - return factory.renderLambdaToString(lambda) - } - - override fun function(f: FunctionReference?): KFunction<*> { - return factory.function(f) - } - - override fun property0(p: PropertyReference0?): KProperty0<*> { - return factory.property0(p) - } - - override fun mutableProperty0(p: MutablePropertyReference0?): KMutableProperty0<*> { - return factory.mutableProperty0(p) - } - - override fun property1(p: PropertyReference1?): KProperty1<*, *> { - return factory.property1(p) - } - - override fun mutableProperty1(p: MutablePropertyReference1?): KMutableProperty1<*, *> { - return factory.mutableProperty1(p) - } - - override fun property2(p: PropertyReference2?): KProperty2<*, *, *> { - return factory.property2(p) - } - - override fun mutableProperty2(p: MutablePropertyReference2?): KMutableProperty2<*, *, *> { - return factory.mutableProperty2(p) - } - - override fun typeOf( - klass: KClassifier?, - arguments: MutableList?, - isMarkedNullable: Boolean - ): KType { - return factory.typeOf(klass, arguments, isMarkedNullable) - } - - override fun typeParameter( - container: Any?, - name: String?, - variance: KVariance?, - isReified: Boolean - ): KTypeParameter { - return factory.typeParameter(container, name, variance, isReified) - } - - override fun setUpperBounds(typeParameter: KTypeParameter?, bounds: MutableList?) { - factory.setUpperBounds(typeParameter, bounds) - } -} From 6cfcf1799ee7f61562c196d2758427e7f9d9d14a Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Tue, 7 May 2024 07:21:32 -0700 Subject: [PATCH 06/92] Compose Flipper plugin improvements Summary: This diff introduces usage of Flipper's `Deferred{}` API in `ComposeNodeDescriptor` which allows Flipper to perform some of the work on background thread. Reviewed By: hick209 Differential Revision: D56997947 fbshipit-source-id: 880ee6e7fa88bac02352883df43b54100f663a17 --- .../descriptors/ComposeNodeDescriptor.kt | 107 ++++++++++-------- .../jetpackcompose/model/ComposeNode.kt | 25 ++-- 2 files changed, 68 insertions(+), 64 deletions(-) diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeNodeDescriptor.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeNodeDescriptor.kt index 65695bb77b4..25a7d807886 100644 --- a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeNodeDescriptor.kt +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeNodeDescriptor.kt @@ -23,7 +23,7 @@ import com.facebook.flipper.plugins.uidebugger.model.Inspectable import com.facebook.flipper.plugins.uidebugger.model.InspectableObject import com.facebook.flipper.plugins.uidebugger.model.InspectableValue import com.facebook.flipper.plugins.uidebugger.model.MetadataId -import com.facebook.flipper.plugins.uidebugger.util.Immediate +import com.facebook.flipper.plugins.uidebugger.util.Deferred import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred import facebook.internal.androidx.compose.ui.inspection.inspector.NodeParameter import facebook.internal.androidx.compose.ui.inspection.inspector.ParameterType @@ -79,65 +79,72 @@ object ComposeNodeDescriptor : NodeDescriptor { } override fun getAttributes(node: ComposeNode): MaybeDeferred> { + return Deferred { + val builder = mutableMapOf() + val props = mutableMapOf() + + props[IdAttributeId] = InspectableValue.Number(node.inspectorNode.id) + props[ViewIdAttributeId] = InspectableValue.Number(node.inspectorNode.viewId) + props[KeyAttributeId] = InspectableValue.Number(node.inspectorNode.key) + props[NameAttributeId] = InspectableValue.Text(node.inspectorNode.name) + props[FilenameAttributeId] = InspectableValue.Text(node.inspectorNode.fileName) + props[PackageHashAttributeId] = InspectableValue.Number(node.inspectorNode.packageHash) + props[LineNumberAttributeId] = InspectableValue.Number(node.inspectorNode.lineNumber) + props[OffsetAttributeId] = InspectableValue.Number(node.inspectorNode.offset) + props[LengthAttributeId] = InspectableValue.Number(node.inspectorNode.length) + + props[BoxAttributeId] = + InspectableValue.Bounds( + Bounds( + node.inspectorNode.left, + node.inspectorNode.top, + node.inspectorNode.width, + node.inspectorNode.height)) + + node.inspectorNode.bounds?.let { bounds -> + val quadBounds = mutableMapOf() + quadBounds[Bounds0AttributeId] = + InspectableValue.Coordinate(Coordinate(bounds.x0, bounds.y0)) + quadBounds[Bounds1AttributeId] = + InspectableValue.Coordinate(Coordinate(bounds.x1, bounds.y1)) + quadBounds[Bounds2AttributeId] = + InspectableValue.Coordinate(Coordinate(bounds.x2, bounds.y2)) + quadBounds[Bounds3AttributeId] = + InspectableValue.Coordinate(Coordinate(bounds.x3, bounds.y3)) + props[BoundsAttributeId] = InspectableObject(quadBounds.toMap()) + } - val builder = mutableMapOf() - val props = mutableMapOf() - - props[IdAttributeId] = InspectableValue.Number(node.inspectorNode.id) - props[ViewIdAttributeId] = InspectableValue.Number(node.inspectorNode.viewId) - props[KeyAttributeId] = InspectableValue.Number(node.inspectorNode.key) - props[NameAttributeId] = InspectableValue.Text(node.inspectorNode.name) - props[FilenameAttributeId] = InspectableValue.Text(node.inspectorNode.fileName) - props[PackageHashAttributeId] = InspectableValue.Number(node.inspectorNode.packageHash) - props[LineNumberAttributeId] = InspectableValue.Number(node.inspectorNode.lineNumber) - props[OffsetAttributeId] = InspectableValue.Number(node.inspectorNode.offset) - props[LengthAttributeId] = InspectableValue.Number(node.inspectorNode.length) - - props[BoxAttributeId] = - InspectableValue.Bounds( - Bounds( - node.inspectorNode.left, - node.inspectorNode.top, - node.inspectorNode.width, - node.inspectorNode.height)) - - node.inspectorNode.bounds?.let { bounds -> - val quadBounds = mutableMapOf() - quadBounds[Bounds0AttributeId] = InspectableValue.Coordinate(Coordinate(bounds.x0, bounds.y0)) - quadBounds[Bounds1AttributeId] = InspectableValue.Coordinate(Coordinate(bounds.x1, bounds.y1)) - quadBounds[Bounds2AttributeId] = InspectableValue.Coordinate(Coordinate(bounds.x2, bounds.y2)) - quadBounds[Bounds3AttributeId] = InspectableValue.Coordinate(Coordinate(bounds.x3, bounds.y3)) - props[BoundsAttributeId] = InspectableObject(quadBounds.toMap()) - } - - val params = mutableMapOf() - node.parameters.forEach { parameter -> - fillNodeParameters(parameter, params, node.inspectorNode.name) - } - builder[ParametersAttributeId] = InspectableObject(params.toMap()) + val params = mutableMapOf() + node.parameters.forEach { parameter -> + fillNodeParameters(parameter, params, node.inspectorNode.name) + } + builder[ParametersAttributeId] = InspectableObject(params.toMap()) - val mergedSemantics = mutableMapOf() - node.mergedSemantics.forEach { parameter -> - fillNodeParameters(parameter, mergedSemantics, node.inspectorNode.name) - } - builder[MergedSemanticsAttributeId] = InspectableObject(mergedSemantics.toMap()) + val mergedSemantics = mutableMapOf() + node.mergedSemantics.forEach { parameter -> + fillNodeParameters(parameter, mergedSemantics, node.inspectorNode.name) + } + builder[MergedSemanticsAttributeId] = InspectableObject(mergedSemantics.toMap()) - val unmergedSemantics = mutableMapOf() - node.unmergedSemantics.forEach { parameter -> - fillNodeParameters(parameter, unmergedSemantics, node.inspectorNode.name) - } - builder[UnmergedSemanticsAttributeId] = InspectableObject(unmergedSemantics.toMap()) + val unmergedSemantics = mutableMapOf() + node.unmergedSemantics.forEach { parameter -> + fillNodeParameters(parameter, unmergedSemantics, node.inspectorNode.name) + } + builder[UnmergedSemanticsAttributeId] = InspectableObject(unmergedSemantics.toMap()) - builder[SectionId] = InspectableObject(props.toMap()) + builder[SectionId] = InspectableObject(props.toMap()) - return Immediate(builder) + builder + } } override fun getInlineAttributes(node: ComposeNode): Map { val attributes = mutableMapOf() if (!node.inspectorNode.inlined) { - node.recompositionCount?.let { attributes["🔄"] = it.toString() } - node.skipCount?.let { attributes["⏭️"] = it.toString() } + node.recompositionCounts?.let { (counts, skips) -> + attributes["🔄"] = counts.toString() + attributes["⏭️"] = skips.toString() + } } else { attributes["inline"] = "true" } diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeNode.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeNode.kt index 2e80c5a0def..8a3e7c0d40a 100644 --- a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeNode.kt +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeNode.kt @@ -39,25 +39,22 @@ class ComposeNode( inspectorNode.width, inspectorNode.height) - val recompositionCount: Int? - - val skipCount: Int? + val recompositionCounts: Pair? by lazy { + recompositionHandler.getCounts(inspectorNode.key, inspectorNode.anchorId)?.let { + Pair(it.count, it.skips) + } + } val children: List = collectChildren() - val parameters: List + val parameters: List by lazy { getNodeParameters(ParameterKind.Normal) } - val mergedSemantics: List - - val unmergedSemantics: List + val mergedSemantics: List by lazy { + getNodeParameters(ParameterKind.MergedSemantics) + } - init { - val count = recompositionHandler.getCounts(inspectorNode.key, inspectorNode.anchorId) - recompositionCount = count?.count - skipCount = count?.skips - parameters = getNodeParameters(ParameterKind.Normal) - mergedSemantics = getNodeParameters(ParameterKind.MergedSemantics) - unmergedSemantics = getNodeParameters(ParameterKind.UnmergedSemantics) + val unmergedSemantics: List by lazy { + getNodeParameters(ParameterKind.UnmergedSemantics) } private fun getNodeParameters(kind: ParameterKind): List { From 958638c956727b0d7f95589092bdbcc2d9bc50c4 Mon Sep 17 00:00:00 2001 From: Pascal Hartig Date: Tue, 7 May 2024 07:24:12 -0700 Subject: [PATCH 07/92] Fix tests (#5602) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: [packer] Fix tests Tests broke recently: https://github.com/facebook/flipper/actions/runs/8943237349/job/24567505699 Pull Request resolved: https://github.com/facebook/flipper/pull/5602 Test Plan: ``` $ cargo test running 4 tests Computing manifest ▕████████████████████▏ test test::test_included_packlist_parses ... ok test tarsum::test::test_nested_archive_tarsum ... ok test tarsum::test::test_differently_ordered_archives ... ok Computing manifest ▕████████████████████▏ test test::test_manifest ... ok test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s ``` Reviewed By: lblasa Differential Revision: D57045431 Pulled By: passy fbshipit-source-id: 84bf5fb78d94ad4dadc24710cbdbcd55a1b1c699 --- packer/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packer/src/main.rs b/packer/src/main.rs index df74996ec90..cd57087c1cc 100644 --- a/packer/src/main.rs +++ b/packer/src/main.rs @@ -405,7 +405,7 @@ mod test { fn test_included_packlist_parses() { let res: PackList = serde_yaml::from_str(DEFAULT_PACKLIST).expect("Default packlist doesn't deserialize"); - assert_eq!(res.0.len(), 4); + assert_eq!(res.0.len(), 6); } #[test] From 345a4dfdec44d16f88228d0aee697a2944640c4d Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Tue, 7 May 2024 11:39:33 -0700 Subject: [PATCH 08/92] Prevent UILabel from becoming active child Summary: context S415547 On ig there is a random UILabel that can beceome the last child of the ui window. This can become active and therefore we dont traverse the correct view. This attempts to mitigate this by making sure we only consider a view for becoming an active child if it itself has subviews Reviewed By: dinhvh, lblasa Differential Revision: D57058208 fbshipit-source-id: 57fcbc3cc6ed2ba02811d73bd5effb8db0b88bd7 --- .../Descriptors/UIView+UIDDescriptor.m | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIView+UIDDescriptor.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIView+UIDDescriptor.m index 189f6ec1116..0095a7d088a 100644 --- a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIView+UIDDescriptor.m +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIView+UIDDescriptor.m @@ -615,27 +615,33 @@ - (void)UID_aggregateAttributes:(nonnull UIDMutableAttributes*)attributes { /** In the context of UIView, the active child is defined as the last view in - the subviews collection. If said item's next responder is a UIViewController, - then return this instead. + the subviews collection that itself has children. This is to avoid random + labels or non container views becoming active child + + We need to account for the fact we inject view controllers into the heierachy, + so if said item's next responder is a different view controller to the current + UIViewController then return the new controller so the active child is + actually one of the children returned to the desktop */ + - (id)UID_activeChild { if (self.isHidden) { return nil; } - if (self.subviews && self.subviews.count > 0) { - UIView* activeChild = [self.subviews lastObject]; - BOOL isController = - [activeChild.nextResponder isKindOfClass:[UIViewController class]]; - - if (isController && activeChild.nextResponder != self.nextResponder) { -/* @cwt-override FIXME[T168581563]: -Wnullable-to-nonnull-conversion */ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnullable-to-nonnull-conversion" - return activeChild.nextResponder; -#pragma clang diagnostic pop + for (UIView* subview in [self.subviews reverseObjectEnumerator]) { + if (subview.subviews && subview.subviews.count > 0) { + BOOL isRootViewOfController = + [subview.nextResponder isKindOfClass:[UIViewController class]]; + + if (isRootViewOfController && + subview.nextResponder != self.nextResponder) { + return (id)[subview nextResponder]; + } + + return subview; + } } - return activeChild; } return nil; } From e4c8c99a9f9652f910623d01f8e01f1ef338b2af Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Tue, 7 May 2024 18:51:38 -0700 Subject: [PATCH 09/92] Set the app id as received from the ws connection Summary: The previous diff sent the app or bundle identifier. This change parses the received field and sets it accordingly. Reviewed By: passy Differential Revision: D56577811 fbshipit-source-id: 8b1fea08ce5a71424c362e11fabb34553ac223d1 --- desktop/flipper-common/src/server-types.tsx | 1 + desktop/flipper-plugin/src/plugin/Plugin.tsx | 1 + .../src/test-utils/test-utils.tsx | 1 + .../src/app-connectivity/ServerController.tsx | 2 ++ .../src/app-connectivity/Utilities.tsx | 6 +++++ .../__tests__/BrowserServerWebSocket.node.tsx | 2 ++ .../__tests__/SecureServerWebSocket.node.tsx | 1 + .../__tests__/ServerWebSocket.node.tsx | 1 + desktop/flipper-server/src/recorder.tsx | 1 + .../src/__tests__/test-utils/MockFlipper.tsx | 1 + .../src/utils/__tests__/exportData.node.tsx | 23 ++++++++++++++++++- 11 files changed, 39 insertions(+), 1 deletion(-) diff --git a/desktop/flipper-common/src/server-types.tsx b/desktop/flipper-common/src/server-types.tsx index c76b4f37152..20852bea5ef 100644 --- a/desktop/flipper-common/src/server-types.tsx +++ b/desktop/flipper-common/src/server-types.tsx @@ -101,6 +101,7 @@ export type UninitializedClient = { export type ClientQuery = { readonly app: string; + readonly app_id?: string; readonly os: DeviceOS; readonly device: string; readonly device_id: string; diff --git a/desktop/flipper-plugin/src/plugin/Plugin.tsx b/desktop/flipper-plugin/src/plugin/Plugin.tsx index 413fe896bd8..0c680cc3766 100644 --- a/desktop/flipper-plugin/src/plugin/Plugin.tsx +++ b/desktop/flipper-plugin/src/plugin/Plugin.tsx @@ -126,6 +126,7 @@ export interface RealFlipperClient { connected: Atom; query: { app: string; + app_id?: string; os: string; device: string; device_id: string; diff --git a/desktop/flipper-plugin/src/test-utils/test-utils.tsx b/desktop/flipper-plugin/src/test-utils/test-utils.tsx index 5c887ee5588..7f1d59aea00 100644 --- a/desktop/flipper-plugin/src/test-utils/test-utils.tsx +++ b/desktop/flipper-plugin/src/test-utils/test-utils.tsx @@ -371,6 +371,7 @@ export function startPlugin>( plugins: new Set([definition.id]), query: { app: appName, + app_id: `com.facebook.flipper.${appName}`, device: deviceName, device_id: testDevice.serial, os: testDevice.serial, diff --git a/desktop/flipper-server/src/app-connectivity/ServerController.tsx b/desktop/flipper-server/src/app-connectivity/ServerController.tsx index bf695161288..1208a922fed 100644 --- a/desktop/flipper-server/src/app-connectivity/ServerController.tsx +++ b/desktop/flipper-server/src/app-connectivity/ServerController.tsx @@ -171,6 +171,7 @@ export class ServerController ): Promise { const { app, + app_id, os, device, device_id, @@ -194,6 +195,7 @@ export class ServerController clientConnection, { app, + app_id, os, device, device_id, diff --git a/desktop/flipper-server/src/app-connectivity/Utilities.tsx b/desktop/flipper-server/src/app-connectivity/Utilities.tsx index ba7127b49fe..0fcda798285 100644 --- a/desktop/flipper-server/src/app-connectivity/Utilities.tsx +++ b/desktop/flipper-server/src/app-connectivity/Utilities.tsx @@ -123,6 +123,11 @@ export function parseClientQuery( return; } + let app_id: string | undefined; + if (typeof query.app_id === 'string') { + app_id = query.app_id; + } + let os: DeviceOS | undefined; if (typeof query.os === 'string') { os = query.os as DeviceOS; @@ -152,6 +157,7 @@ export function parseClientQuery( device_id, device, app, + app_id, os, medium: transformCertificateExchangeMediumToType(medium), sdk_version, diff --git a/desktop/flipper-server/src/app-connectivity/__tests__/BrowserServerWebSocket.node.tsx b/desktop/flipper-server/src/app-connectivity/__tests__/BrowserServerWebSocket.node.tsx index 702c85e665a..02ca8cdf999 100644 --- a/desktop/flipper-server/src/app-connectivity/__tests__/BrowserServerWebSocket.node.tsx +++ b/desktop/flipper-server/src/app-connectivity/__tests__/BrowserServerWebSocket.node.tsx @@ -80,6 +80,7 @@ describe('BrowserServerWebSocket', () => { device, os, app, + app_id: `com.facebook.flipper.${app}`, sdk_version: sdkVersion, medium: 'NONE', }; @@ -182,6 +183,7 @@ describe('BrowserServerWebSocket', () => { device, os: 'MacOS', app: device, + app_id: `com.facebook.flipper.${device}`, sdk_version: 4, medium: 'NONE', }; diff --git a/desktop/flipper-server/src/app-connectivity/__tests__/SecureServerWebSocket.node.tsx b/desktop/flipper-server/src/app-connectivity/__tests__/SecureServerWebSocket.node.tsx index 8b66bc296c0..2e97419cf0d 100644 --- a/desktop/flipper-server/src/app-connectivity/__tests__/SecureServerWebSocket.node.tsx +++ b/desktop/flipper-server/src/app-connectivity/__tests__/SecureServerWebSocket.node.tsx @@ -78,6 +78,7 @@ describe('SecureServerWebSocket', () => { device, os, app, + app_id: `com.facebook.flipper.${app}`, sdk_version: sdkVersion, csr, csr_path: csrPath, diff --git a/desktop/flipper-server/src/app-connectivity/__tests__/ServerWebSocket.node.tsx b/desktop/flipper-server/src/app-connectivity/__tests__/ServerWebSocket.node.tsx index 2eaac0f7491..2fe2f45e9db 100644 --- a/desktop/flipper-server/src/app-connectivity/__tests__/ServerWebSocket.node.tsx +++ b/desktop/flipper-server/src/app-connectivity/__tests__/ServerWebSocket.node.tsx @@ -59,6 +59,7 @@ describe('ServerWebSocket', () => { device, os, app, + app_id: `com.facebook.flipper.${app}`, sdk_version: sdkVersion, medium: 'WWW', }; diff --git a/desktop/flipper-server/src/recorder.tsx b/desktop/flipper-server/src/recorder.tsx index 5b8497a72b9..2abc1494923 100644 --- a/desktop/flipper-server/src/recorder.tsx +++ b/desktop/flipper-server/src/recorder.tsx @@ -33,6 +33,7 @@ class Recorder { private flipperServer_: FlipperServerImpl | undefined; private undefinedClientQuery_: ClientQuery = { app: 'NONE', + app_id: 'NONE', device: 'NONE', medium: 'NONE', os: 'Browser', diff --git a/desktop/flipper-ui/src/__tests__/test-utils/MockFlipper.tsx b/desktop/flipper-ui/src/__tests__/test-utils/MockFlipper.tsx index ee68fa72506..98e66aceee0 100644 --- a/desktop/flipper-ui/src/__tests__/test-utils/MockFlipper.tsx +++ b/desktop/flipper-ui/src/__tests__/test-utils/MockFlipper.tsx @@ -171,6 +171,7 @@ export default class MockFlipper { } query = query ?? { app: name ?? `serial_${++this._clientCounter}`, + app_id: name ?? 'NONE', os: 'Android', device: device.title, device_id: device.serial, diff --git a/desktop/flipper-ui/src/utils/__tests__/exportData.node.tsx b/desktop/flipper-ui/src/utils/__tests__/exportData.node.tsx index 6f1f602195b..8b32f3be085 100644 --- a/desktop/flipper-ui/src/utils/__tests__/exportData.node.tsx +++ b/desktop/flipper-ui/src/utils/__tests__/exportData.node.tsx @@ -108,6 +108,7 @@ function generateClientFromClientWithSalt( id: identifier, query: { app, + app_id: `com.facebook.flipper.${app}`, os, device, device_id: `${salt}-${device_id}`, @@ -121,7 +122,14 @@ function generateClientFromDevice(device: Device, app: string): ClientExport { const identifier = generateClientIdentifier(device, app); return { id: identifier, - query: {app, os, device: deviceType, device_id: serial, medium: 'NONE'}, + query: { + app, + app_id: `com.facebook.flipper.${app}`, + os, + device: deviceType, + device_id: serial, + medium: 'NONE', + }, }; } @@ -734,6 +742,7 @@ test('test determinePluginsToProcess for mutilple clients having plugins present generateClientIdentifier(device1, 'app'), { app: 'app', + app_id: 'com.facebook.flipper.app', os: 'iOS', device: 'TestiPhone', device_id: 'serial1', @@ -750,6 +759,7 @@ test('test determinePluginsToProcess for mutilple clients having plugins present generateClientIdentifier(device1, 'app2'), { app: 'app2', + app_id: 'com.facebook.flipper.app2', os: 'iOS', device: 'TestiPhone', device_id: 'serial1', @@ -766,6 +776,7 @@ test('test determinePluginsToProcess for mutilple clients having plugins present generateClientIdentifier(device1, 'app3'), { app: 'app3', + app_id: 'com.facebook.flipper.app3', os: 'iOS', device: 'TestiPhone', device_id: 'serial1', @@ -826,6 +837,7 @@ test('test determinePluginsToProcess for no selected plugin present in any clien generateClientIdentifier(device1, 'app'), { app: 'app', + app_id: 'com.facebook.flipper.app', os: 'iOS', device: 'TestiPhone', device_id: 'serial1', @@ -842,6 +854,7 @@ test('test determinePluginsToProcess for no selected plugin present in any clien generateClientIdentifier(device1, 'app2'), { app: 'app2', + app_id: 'com.facebook.flipper.app2', os: 'iOS', device: 'TestiPhone', device_id: 'serial1', @@ -885,6 +898,7 @@ test('test determinePluginsToProcess for multiple clients on same device', async generateClientIdentifier(device1, 'app'), { app: 'app', + app_id: 'com.facebook.flipper.app', os: 'iOS', device: 'TestiPhone', device_id: 'serial1', @@ -901,6 +915,7 @@ test('test determinePluginsToProcess for multiple clients on same device', async generateClientIdentifier(device1, 'app2'), { app: 'app2', + app_id: 'com.facebook.flipper.app2', os: 'iOS', device: 'TestiPhone', device_id: 'serial1', @@ -949,6 +964,7 @@ test('test determinePluginsToProcess for multiple clients on different device', generateClientIdentifier(device1, 'app'), { app: 'app', + app_id: 'com.facebook.flipper.app', os: 'iOS', device: 'TestiPhone', device_id: 'serial1', @@ -965,6 +981,7 @@ test('test determinePluginsToProcess for multiple clients on different device', generateClientIdentifier(device1, 'app2'), { app: 'app1', + app_id: 'com.facebook.flipper.app1', os: 'iOS', device: 'TestiPhone', device_id: 'serial1', @@ -981,6 +998,7 @@ test('test determinePluginsToProcess for multiple clients on different device', generateClientIdentifier(device2, 'app'), { app: 'app', + app_id: 'com.facebook.flipper.app', os: 'iOS', device: 'TestiPhone', device_id: 'serial2', @@ -997,6 +1015,7 @@ test('test determinePluginsToProcess for multiple clients on different device', generateClientIdentifier(device2, 'app2'), { app: 'app1', + app_id: 'com.facebook.flipper.app1', os: 'iOS', device: 'TestiPhone', device_id: 'serial2', @@ -1069,6 +1088,7 @@ test('test determinePluginsToProcess to ignore archived clients', async () => { generateClientIdentifier(selectedDevice, 'app'), { app: 'app', + app_id: 'com.facebook.flipper.app', os: 'iOS', device: 'TestiPhone', device_id: 'serial', @@ -1085,6 +1105,7 @@ test('test determinePluginsToProcess to ignore archived clients', async () => { generateClientIdentifier(archivedDevice, 'app'), { app: 'app', + app_id: 'com.facebook.flipper.app', os: 'iOS', device: 'TestiPhone', device_id: 'serial-archived', From f58a91f7bebce8dadb21c80238258495a104aad8 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Tue, 7 May 2024 18:51:38 -0700 Subject: [PATCH 10/92] Expose app bundle id Summary: It used to use the id, which is definitely not the app or bundle identifier. It gracefully handles old clients by using the previously used id if the app_id is not found. Reviewed By: lblasa Differential Revision: D56578429 fbshipit-source-id: e7a234cea1ca4a70b5840bca9a817b174390a3df --- desktop/flipper-plugin/src/plugin/Plugin.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/desktop/flipper-plugin/src/plugin/Plugin.tsx b/desktop/flipper-plugin/src/plugin/Plugin.tsx index 0c680cc3766..b2ce726fdfd 100644 --- a/desktop/flipper-plugin/src/plugin/Plugin.tsx +++ b/desktop/flipper-plugin/src/plugin/Plugin.tsx @@ -45,6 +45,11 @@ export interface PluginClient< */ readonly appId: string; + /** + * Identifier that uniquely identifies the application bundle + */ + readonly bundleId: string; + /** * Registered name for the connected application */ @@ -195,6 +200,9 @@ export class SandyPluginInstance extends BasePluginInstance { get appId() { return realClient.id; }, + get bundleId() { + return realClient.query.app_id ?? 'unknown'; + }, get appName() { return realClient.query.app; }, From fabbe8bf261bec8c4614d15c9469de9ff8948768 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Tue, 7 May 2024 18:51:38 -0700 Subject: [PATCH 11/92] use bundle id when restarting ios app instead of app name Summary: App names can collide, bundle id is more unique Reviewed By: lblasa Differential Revision: D56964151 fbshipit-source-id: 5b18662c702d8fbf1c85ce8167c796af61126a80 --- desktop/flipper-server/src/FlipperServerImpl.tsx | 4 ++-- desktop/flipper-server/src/devices/ios/IOSBridge.tsx | 6 +++--- .../flipper-server/src/devices/ios/iOSDeviceManager.tsx | 9 ++------- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/desktop/flipper-server/src/FlipperServerImpl.tsx b/desktop/flipper-server/src/FlipperServerImpl.tsx index 351defef36c..45d25b7a185 100644 --- a/desktop/flipper-server/src/FlipperServerImpl.tsx +++ b/desktop/flipper-server/src/FlipperServerImpl.tsx @@ -564,9 +564,9 @@ export class FlipperServerImpl implements FlipperServer { assertNotNull(this.ios); return this.ios.launchSimulator(udid); }, - 'ios-launch-app': async (udid, appName) => { + 'ios-launch-app': async (udid, bundleId) => { assertNotNull(this.ios); - return this.ios.launchApp(udid, appName); + return this.ios.launchApp(udid, bundleId); }, 'ios-idb-kill': async () => { assertNotNull(this.ios); diff --git a/desktop/flipper-server/src/devices/ios/IOSBridge.tsx b/desktop/flipper-server/src/devices/ios/IOSBridge.tsx index 040a341b840..0e7a3911a88 100644 --- a/desktop/flipper-server/src/devices/ios/IOSBridge.tsx +++ b/desktop/flipper-server/src/devices/ios/IOSBridge.tsx @@ -150,9 +150,9 @@ export class IDBBridge implements IOSBridge { await this._execIdb(`install ${ipaPath} --udid ${serial}`); } - async openApp(serial: string, name: string): Promise { - console.log(`Opening app via IDB ${name} ${serial}`); - await this._execIdb(`launch ${name} --udid ${serial} -f`); + async openApp(serial: string, bundleId: string): Promise { + console.log(`Opening app via IDB ${bundleId} ${serial}`); + await this._execIdb(`launch --udid ${serial} ${bundleId} -f`); } async getActiveDevices(bootedOnly: boolean): Promise { diff --git a/desktop/flipper-server/src/devices/ios/iOSDeviceManager.tsx b/desktop/flipper-server/src/devices/ios/iOSDeviceManager.tsx index 78a71d8ef3d..9744f887af4 100644 --- a/desktop/flipper-server/src/devices/ios/iOSDeviceManager.tsx +++ b/desktop/flipper-server/src/devices/ios/iOSDeviceManager.tsx @@ -202,15 +202,10 @@ export class IOSDeviceManager { } } - async launchApp(udid: string, appName: string) { + async launchApp(udid: string, bundleId: string) { try { const bridge = await this.getBridge(); - const installedApps = await bridge.getInstalledApps(udid); - const app = installedApps.find((x) => x.name === appName); - if (!app) { - throw new Error(`Could not find requested app "${appName}"`); - } - await bridge.openApp(udid, app.bundleID); + await bridge.openApp(udid, bundleId); } catch (e) { console.warn('Failed to launch simulator:', e); } From 6a4d1e81016738f7e8503d9e716afd8ec7a9a09c Mon Sep 17 00:00:00 2001 From: generatedunixname267772532268223 Date: Wed, 8 May 2024 04:10:38 -0700 Subject: [PATCH 12/92] Enable for targets in xplat/sonar/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util Reviewed By: LukeDefeo Differential Revision: D57094780 fbshipit-source-id: 81aeedcfa6f4ecd5b07e2698b1e0cd62c2feceb8 --- .../facebook/internal/androidx/compose/ui/inspection/util/BUCK | 1 + 1 file changed, 1 insertion(+) diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/BUCK b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/BUCK index 105cca8ed1a..82be983656a 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/BUCK +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/BUCK @@ -2,6 +2,7 @@ load("@fbsource//tools/build_defs/android:fb_android_library.bzl", "fb_android_l fb_android_library( name = "util", + abi_generation_mode = "source_only", oncall = "flipper", deps = [ "//third-party/java/androidx/collection/collection:collection", From fef2ee156caef3e1535027e0d5248341670c5188 Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Wed, 8 May 2024 04:14:45 -0700 Subject: [PATCH 13/92] Add an API for declaring custom actions Summary: This diff adds an API that allows Flipper plugins for adding custom actions that will be displayed in Flipper Desktop. Reviewed By: LukeDefeo Differential Revision: D57046356 fbshipit-source-id: 02bc3ac68a6fdf8ef8c0478586bacba9334b2335 --- .../uidebugger/UIDebuggerFlipperPlugin.kt | 71 ++++++++++++++++++- .../uidebugger/core/DecorViewTracker.kt | 4 ++ .../plugins/uidebugger/core/UIDContext.kt | 17 +++++ .../plugins/uidebugger/model/CustomActions.kt | 58 +++++++++++++++ .../plugins/uidebugger/model/Events.kt | 6 +- 5 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/CustomActions.kt diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt index 339cca83687..1371b5afd85 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt @@ -15,10 +15,13 @@ import com.facebook.flipper.plugins.uidebugger.core.* import com.facebook.flipper.plugins.uidebugger.descriptors.ApplicationRefDescriptor import com.facebook.flipper.plugins.uidebugger.descriptors.CompoundTypeHint import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister +import com.facebook.flipper.plugins.uidebugger.model.Action +import com.facebook.flipper.plugins.uidebugger.model.ActionIcon import com.facebook.flipper.plugins.uidebugger.model.InitEvent import com.facebook.flipper.plugins.uidebugger.model.MetadataId import com.facebook.flipper.plugins.uidebugger.model.MetadataUpdateEvent import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule const val LogTag = "ui-debugger" @@ -69,13 +72,45 @@ class UIDebuggerFlipperPlugin(val context: UIDContext) : FlipperPlugin { } } + connection.receive("customActionGroup") { args, responder -> + try { + val customActionGroupIndex = args.getInt("customActionGroupIndex") + val customAction = context.customActionGroups[customActionGroupIndex] + + val customActionIndex = args.getInt("customActionIndex") + when (val item = customAction.actions[customActionIndex]) { + is Action.UnitAction -> { + item.action() + context.decorViewTracker.requestTraversal() + responder.success() + } + is Action.BooleanAction -> { + val newBooleanActionValue = args.getBoolean("newBooleanActionValue") + val result = item.action(newBooleanActionValue) + context.decorViewTracker.requestTraversal() + responder.success(FlipperObject.Builder().put("result", result).build()) + } + } + } catch (exception: Exception) { + + val errorResponse = + FlipperObject.Builder() + .put("errorType", exception.javaClass) + .put("errorMessage", exception.message) + .put("stackTrace", exception.stackTraceToString()) + .build() + responder.error(errorResponse) + } + } + connection.send( InitEvent.name, - Json.encodeToString( + INIT_EVENT_JSON.encodeToString( InitEvent.serializer(), InitEvent( ApplicationRefDescriptor.getId(context.applicationRef), - context.frameworkEventMetadata))) + context.frameworkEventMetadata, + context.customActionGroups))) connection.send( MetadataUpdateEvent.name, @@ -106,4 +141,36 @@ class UIDebuggerFlipperPlugin(val context: UIDContext) : FlipperPlugin { override fun runInBackground(): Boolean { return false } + + companion object { + private val INIT_EVENT_JSON = Json { + serializersModule = SerializersModule { + polymorphic( + Action::class, + Action.UnitAction::class, + Action.UnitAction.serializer(), + ) + polymorphic( + Action::class, + Action.BooleanAction::class, + Action.BooleanAction.serializer(), + ) + polymorphic( + ActionIcon::class, + ActionIcon.Local::class, + ActionIcon.Local.serializer(), + ) + polymorphic( + ActionIcon::class, + ActionIcon.Antd::class, + ActionIcon.Antd.serializer(), + ) + polymorphic( + ActionIcon::class, + ActionIcon.Fb::class, + ActionIcon.Fb.serializer(), + ) + } + } + } } diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/DecorViewTracker.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/DecorViewTracker.kt index a4093a92c08..9ac89f0a536 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/DecorViewTracker.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/DecorViewTracker.kt @@ -104,6 +104,10 @@ class DecorViewTracker(private val context: UIDContext, private val snapshotter: Log.i(LogTag, "Starting tracking root views, currently ${Curtains.rootViews.size} root views") } + fun requestTraversal() { + preDrawListener?.onPreDraw() + } + fun stop() { Curtains.onRootViewsChangedListeners.clear() currentDecorView?.viewTreeObserver?.removeOnPreDrawListener(preDrawListener) diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/UIDContext.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/UIDContext.kt index c92ef1c3ec4..c93bd0ad427 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/UIDContext.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/UIDContext.kt @@ -13,6 +13,9 @@ import android.util.Log import com.facebook.flipper.core.FlipperConnection import com.facebook.flipper.plugins.uidebugger.LogTag import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister +import com.facebook.flipper.plugins.uidebugger.model.ActionIcon +import com.facebook.flipper.plugins.uidebugger.model.CustomActionGroup +import com.facebook.flipper.plugins.uidebugger.model.CustomActionsScope import com.facebook.flipper.plugins.uidebugger.model.FrameworkEvent import com.facebook.flipper.plugins.uidebugger.model.FrameworkEventMetadata import com.facebook.flipper.plugins.uidebugger.model.TraversalError @@ -35,7 +38,11 @@ class UIDContext( val attributeEditor = AttributeEditor(applicationRef, descriptorRegister) val bitmapPool = BitmapPool() + val customActionGroups: List + get() = _customActionGroups + private val canvasSnapshotter = CanvasSnapshotter(bitmapPool) + private val _customActionGroups = mutableListOf() private val snapshotter = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { @@ -74,6 +81,16 @@ class UIDContext( synchronized(pendingFrameworkEvents) { pendingFrameworkEvents.clear() } } + fun addCustomActionGroup( + title: String, + actionIcon: ActionIcon, + block: CustomActionsScope.() -> Unit + ) { + val scope = CustomActionsScope() + scope.block() + _customActionGroups.add(CustomActionGroup(title, actionIcon, scope.actions)) + } + companion object { fun create(application: Application): UIDContext { return UIDContext( diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/CustomActions.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/CustomActions.kt new file mode 100644 index 00000000000..4207b1d8951 --- /dev/null +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/CustomActions.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.flipper.plugins.uidebugger.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient + +@Serializable +data class CustomActionGroup( + val title: String, + val actionIcon: ActionIcon, + val actions: List +) + +sealed interface Action { + @Serializable + @SerialName("UnitAction") + data class UnitAction(val title: String, @Transient val action: () -> Unit = {}) : Action + + @Serializable + @SerialName("BooleanAction") + data class BooleanAction( + val title: String, + val initialValue: Boolean, + @Transient val action: (Boolean) -> Boolean = { it }, + ) : Action +} + +sealed interface ActionIcon { + @Serializable + @SerialName("Local") + data class Local(@SerialName("iconPath") val iconFullPath: String) : ActionIcon + + @Serializable @SerialName("Antd") data class Antd(val iconName: String) : ActionIcon + + @Serializable @SerialName("Fb") data class Fb(val iconName: String) : ActionIcon +} + +class CustomActionsScope { + val actions: List + get() = _actions + + private val _actions = mutableListOf() + + fun unitAction(title: String, action: () -> Unit) { + _actions.add(Action.UnitAction(title, action)) + } + + fun booleanAction(title: String, initialValue: Boolean, action: (Boolean) -> Boolean) { + _actions.add(Action.BooleanAction(title, initialValue, action)) + } +} diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/Events.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/Events.kt index 957de38a622..39a37e9e93e 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/Events.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/Events.kt @@ -10,7 +10,11 @@ package com.facebook.flipper.plugins.uidebugger.model import com.facebook.flipper.plugins.uidebugger.descriptors.Id @kotlinx.serialization.Serializable -class InitEvent(val rootId: Id, val frameworkEventMetadata: List) { +class InitEvent( + val rootId: Id, + val frameworkEventMetadata: List, + val customActionGroups: List +) { companion object { const val name = "init" } From 934e64e676897b94cb5f3f5157aa3b27b7ac0e30 Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Wed, 8 May 2024 04:14:45 -0700 Subject: [PATCH 14/92] Add custom action for controlling showing system nodes Summary: This diff uses the new Flipper API to add a custom action in Flipper Desktop app that will allow the developer to turn on/off showing Compose system nodes. Reviewed By: LukeDefeo Differential Revision: D57048841 fbshipit-source-id: 2d3fff0c215a94d95b69892b5f415f26d960c65e --- .../jetpackcompose/UIDebuggerComposeSupport.kt | 14 ++++++++++++++ .../descriptors/AbstractComposeViewDescriptor.kt | 5 ++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt index a0f99fe3458..2b9518d291f 100644 --- a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt @@ -18,6 +18,7 @@ import com.facebook.flipper.plugins.jetpackcompose.model.ComposeInnerViewNode import com.facebook.flipper.plugins.jetpackcompose.model.ComposeNode import com.facebook.flipper.plugins.uidebugger.core.UIDContext import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister +import com.facebook.flipper.plugins.uidebugger.model.ActionIcon import com.facebook.soloader.SoLoader const val JetpackComposeTag = "Compose" @@ -38,6 +39,7 @@ object UIDebuggerComposeSupport { } fun enable(context: UIDContext) { + addCustomActions(context) addDescriptors(context.descriptorRegister) } @@ -47,6 +49,18 @@ object UIDebuggerComposeSupport { register.register(ComposeInnerViewNode::class.java, ComposeInnerViewDescriptor) } + private fun addCustomActions(context: UIDContext) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + context.addCustomActionGroup("Compose", ActionIcon.Local("icons/compose-logo.png")) { + booleanAction("Hide System Nodes", AbstractComposeViewDescriptor.hideSystemNodes) { newValue + -> + AbstractComposeViewDescriptor.hideSystemNodes = newValue + newValue + } + } + } + } + private fun enableDebugInspectorInfo() { // Set isDebugInspectorInfoEnabled to true via reflection such that Redex and R8 cannot see the // assignment. This allows the InspectorInfo lambdas to be stripped from release builds. diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/AbstractComposeViewDescriptor.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/AbstractComposeViewDescriptor.kt index 02f05bc3241..edce98ef544 100644 --- a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/AbstractComposeViewDescriptor.kt +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/AbstractComposeViewDescriptor.kt @@ -24,6 +24,9 @@ import java.io.IOException @RequiresApi(Build.VERSION_CODES.Q) object AbstractComposeViewDescriptor : ChainedDescriptor() { + + internal var hideSystemNodes: Boolean = true + private val recompositionHandler by lazy { RecompositionHandler(DefaultArtTooling("Flipper")).apply { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { @@ -67,7 +70,7 @@ object AbstractComposeViewDescriptor : ChainedDescriptor() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val layoutInspector = LayoutInspectorTree() - layoutInspector.hideSystemNodes = true + layoutInspector.hideSystemNodes = hideSystemNodes val composeNodes = try { transform(child, layoutInspector.convert(child), layoutInspector) From 2a83b082da7f4b0fa51eafa990de597aff68a22b Mon Sep 17 00:00:00 2001 From: generatedunixname267772532268223 Date: Wed, 8 May 2024 06:15:37 -0700 Subject: [PATCH 15/92] Enable for targets in xplat/sonar/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection Differential Revision: D57095238 fbshipit-source-id: 5609ac72309d3bef1c75e1816d0e3d5a901a08be --- .../java/facebook/internal/androidx/compose/ui/inspection/BUCK | 1 + 1 file changed, 1 insertion(+) diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/BUCK b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/BUCK index a2aea3c540a..ae86bb93a74 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/BUCK +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/BUCK @@ -2,6 +2,7 @@ load("@fbsource//tools/build_defs/android:fb_android_library.bzl", "fb_android_l fb_android_library( name = "ui-inspection", + abi_generation_mode = "source_only", create_suffixed_alias = True, dataclass_generate = { "mode": "EXPLICIT", From b92446d6d1270b3f2a8ffe4096e3a3715427438f Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Wed, 8 May 2024 08:48:15 -0700 Subject: [PATCH 16/92] Remove usage of ultralight from NT uidebugger Summary: using Ultralight is problematic for uidebugger since UIDebugger is used in contexts where ultralight isnt (Open source & flipper / litho sample apps in buck) The new approach is a tad hacky, we add a descriptor that handles debug component (the litho wrapper for litho Component class), this new descriptor can also handle NT if the underlying component is an NT component, this is recognised by the WrappedNTComponent class. If so we add the additional parts for the desktop. This descriptor is manually added in apps that have NT, currently just fb4a The hacky part is we overrite the original descriptor in the registry, but given how DebugComponent works and is used regardless of if we are NT / Litho i dont see a better way Differential Revision: D57105063 fbshipit-source-id: 63e291c22c566ff52277013fe1b945f8c41464c1 --- ...ILithoDebugComponentDescriptorExtension.kt | 19 -------------- .../litho/UIDebuggerLithoSupport.kt | 11 ++++++-- .../descriptors/DebugComponentDescriptor.kt | 25 +------------------ 3 files changed, 10 insertions(+), 45 deletions(-) delete mode 100644 android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/ILithoDebugComponentDescriptorExtension.kt diff --git a/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/ILithoDebugComponentDescriptorExtension.kt b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/ILithoDebugComponentDescriptorExtension.kt deleted file mode 100644 index eede79d04bf..00000000000 --- a/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/ILithoDebugComponentDescriptorExtension.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.flipper.plugins.uidebugger.litho - -import com.facebook.inject.statics.BoundSetStatic -import com.facebook.litho.DebugComponent -import kotlinx.serialization.json.JsonObject - -@BoundSetStatic -interface ILithoDebugComponentDescriptorExtension { - fun getExtraTags(node: DebugComponent): Set? - - fun getExtraHiddenAttributes(node: DebugComponent): JsonObject? -} diff --git a/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/UIDebuggerLithoSupport.kt b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/UIDebuggerLithoSupport.kt index b43e8de4c12..edfc368ba28 100644 --- a/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/UIDebuggerLithoSupport.kt +++ b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/UIDebuggerLithoSupport.kt @@ -10,7 +10,11 @@ package com.facebook.flipper.plugins.uidebugger.litho import com.facebook.flipper.plugins.uidebugger.core.ConnectionListener import com.facebook.flipper.plugins.uidebugger.core.UIDContext import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister -import com.facebook.flipper.plugins.uidebugger.litho.descriptors.* +import com.facebook.flipper.plugins.uidebugger.litho.descriptors.ComponentTreeDescriptor +import com.facebook.flipper.plugins.uidebugger.litho.descriptors.DebugComponentDescriptor +import com.facebook.flipper.plugins.uidebugger.litho.descriptors.LithoViewDescriptor +import com.facebook.flipper.plugins.uidebugger.litho.descriptors.MatrixDrawableDescriptor +import com.facebook.flipper.plugins.uidebugger.litho.descriptors.TextDrawableDescriptor import com.facebook.flipper.plugins.uidebugger.model.FrameworkEvent import com.facebook.flipper.plugins.uidebugger.model.FrameworkEventMetadata import com.facebook.litho.ComponentTree @@ -137,9 +141,12 @@ object UIDebuggerLithoSupport { event.attributeOrNull(attributeName)?.let { attributes[attributeName] = it.toString() } } + lateinit var DebugComponentDescritpor: DebugComponentDescriptor + private fun addDescriptors(register: DescriptorRegister) { register.register(LithoView::class.java, LithoViewDescriptor) - register.register(DebugComponent::class.java, DebugComponentDescriptor(register)) + DebugComponentDescritpor = DebugComponentDescriptor(register) + register.register(DebugComponent::class.java, DebugComponentDescritpor) register.register(TextDrawable::class.java, TextDrawableDescriptor) register.register(MatrixDrawable::class.java, MatrixDrawableDescriptor) register.register(ComponentTree::class.java, ComponentTreeDescriptor(register)) diff --git a/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/DebugComponentDescriptor.kt b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/DebugComponentDescriptor.kt index 9b47585f749..2aa0234b054 100644 --- a/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/DebugComponentDescriptor.kt +++ b/android/plugins/litho/src/main/java/com/facebook/flipper/plugins/uidebugger/litho/descriptors/DebugComponentDescriptor.kt @@ -16,7 +16,6 @@ import com.facebook.flipper.plugins.uidebugger.descriptors.Id import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor import com.facebook.flipper.plugins.uidebugger.descriptors.OffsetChild -import com.facebook.flipper.plugins.uidebugger.litho.ILithoDebugComponentDescriptorExtensionStatic import com.facebook.flipper.plugins.uidebugger.litho.LithoMountableTag import com.facebook.flipper.plugins.uidebugger.litho.LithoTag import com.facebook.flipper.plugins.uidebugger.litho.descriptors.props.ComponentDataExtractor @@ -250,14 +249,6 @@ class DebugComponentDescriptor(val register: DescriptorRegister) : NodeDescripto override fun getTags(node: DebugComponent): Set { val tags: MutableSet = mutableSetOf(LithoTag) - - for (id in ILithoDebugComponentDescriptorExtensionStatic.getKeys()) { - val extraTags = ILithoDebugComponentDescriptorExtensionStatic.getExtraTags(id, node) - if (extraTags != null) { - tags.addAll(extraTags) - } - } - if (node.component.mountType != Component.MountType.NONE) { tags.add(LithoMountableTag) } @@ -312,21 +303,7 @@ class DebugComponentDescriptor(val register: DescriptorRegister) : NodeDescripto return mountingData } - private fun mergeJsonObjects(obj1: JsonObject, obj2: JsonObject): JsonObject { - return JsonObject(obj1.toMap() + obj2.toMap()) - } - - override fun getHiddenAttributes(node: DebugComponent): JsonObject? { - var hiddenAttributes = JsonObject(mapOf()) - for (id in ILithoDebugComponentDescriptorExtensionStatic.getKeys()) { - val extraHiddenAttributes = - ILithoDebugComponentDescriptorExtensionStatic.getExtraHiddenAttributes(id, node) - if (extraHiddenAttributes != null) { - hiddenAttributes = mergeJsonObjects(hiddenAttributes, extraHiddenAttributes) - } - } - return hiddenAttributes - } + override fun getHiddenAttributes(node: DebugComponent): JsonObject? = null class OverrideData( val metadataPath: List, From 70dd79c000729e2bf9f3a20ddda99d8920ad5775 Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Thu, 9 May 2024 05:42:20 -0700 Subject: [PATCH 17/92] Make icon required for Custom Actions of unit type Summary: As per title, making the icon to be required for unit actions. Reviewed By: LukeDefeo Differential Revision: D57157035 fbshipit-source-id: 14723811ac45219675cb171a76deebfd0e9346d0 --- .../flipper/plugins/uidebugger/model/CustomActions.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/CustomActions.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/CustomActions.kt index 4207b1d8951..bf97568ed9c 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/CustomActions.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/CustomActions.kt @@ -21,7 +21,11 @@ data class CustomActionGroup( sealed interface Action { @Serializable @SerialName("UnitAction") - data class UnitAction(val title: String, @Transient val action: () -> Unit = {}) : Action + data class UnitAction( + val title: String, + val actionIcon: ActionIcon, + @Transient val action: () -> Unit = {} + ) : Action @Serializable @SerialName("BooleanAction") @@ -48,8 +52,8 @@ class CustomActionsScope { private val _actions = mutableListOf() - fun unitAction(title: String, action: () -> Unit) { - _actions.add(Action.UnitAction(title, action)) + fun unitAction(title: String, actionIcon: ActionIcon, action: () -> Unit) { + _actions.add(Action.UnitAction(title, actionIcon, action)) } fun booleanAction(title: String, initialValue: Boolean, action: (Boolean) -> Boolean) { From 62f9063040e49761ce8090aebf8de17b98c43c8e Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Thu, 9 May 2024 06:24:07 -0700 Subject: [PATCH 18/92] Add API for notifying about additional node inspection requests Summary: This diff adds an API that allows Flipper Plugins to tell Flipper Desktop app that some of the attributes can be additionally analyzed on demand. These nodes can be selected in the desktop app in order to perform additional, computation heavy analysis on a given node. Reviewed By: LukeDefeo Differential Revision: D57148570 fbshipit-source-id: 7cfb963844f571c447d50d7b69144213091f5d84 --- .../uidebugger/UIDebuggerFlipperPlugin.kt | 27 ++++++++++++++ .../uidebugger/core/LayoutTraversal.kt | 24 ++++++++++--- .../uidebugger/descriptors/NodeDescriptor.kt | 35 +++++++++++++++++-- .../flipper/plugins/uidebugger/model/Node.kt | 8 +++++ 4 files changed, 87 insertions(+), 7 deletions(-) diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt index 1371b5afd85..f2119a5cfcb 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt @@ -14,12 +14,14 @@ import com.facebook.flipper.core.FlipperPlugin import com.facebook.flipper.plugins.uidebugger.core.* import com.facebook.flipper.plugins.uidebugger.descriptors.ApplicationRefDescriptor import com.facebook.flipper.plugins.uidebugger.descriptors.CompoundTypeHint +import com.facebook.flipper.plugins.uidebugger.descriptors.Id import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister import com.facebook.flipper.plugins.uidebugger.model.Action import com.facebook.flipper.plugins.uidebugger.model.ActionIcon import com.facebook.flipper.plugins.uidebugger.model.InitEvent import com.facebook.flipper.plugins.uidebugger.model.MetadataId import com.facebook.flipper.plugins.uidebugger.model.MetadataUpdateEvent +import java.lang.IllegalStateException import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule @@ -103,6 +105,31 @@ class UIDebuggerFlipperPlugin(val context: UIDContext) : FlipperPlugin { } } + connection.receive("additionalNodeInspectionChange") { args, responder -> + try { + val changeType = args.getString("changeType") + val nodeId: Id = args.getInt("nodeId") + + when (changeType) { + "Add" -> context.layoutTraversal.additionalNodeInspectionIds.add(nodeId) + "Remove" -> context.layoutTraversal.additionalNodeInspectionIds.remove(nodeId) + else -> throw IllegalStateException("Unknown change type: $changeType") + } + + context.decorViewTracker.requestTraversal() + responder.success() + } catch (exception: Exception) { + + val errorResponse = + FlipperObject.Builder() + .put("errorType", exception.javaClass) + .put("errorMessage", exception.message) + .put("stackTrace", exception.stackTraceToString()) + .build() + responder.error(errorResponse) + } + } + connection.send( InitEvent.name, INIT_EVENT_JSON.encodeToString( diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt index 7657f694e96..437c587ccff 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt @@ -8,9 +8,11 @@ package com.facebook.flipper.plugins.uidebugger.core import android.util.Log +import androidx.collection.mutableScatterSetOf import com.facebook.flipper.plugins.uidebugger.LogTag import com.facebook.flipper.plugins.uidebugger.descriptors.Id import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor +import com.facebook.flipper.plugins.uidebugger.model.AdditionalDataStatus import com.facebook.flipper.plugins.uidebugger.model.Node import com.facebook.flipper.plugins.uidebugger.model.TraversalError import com.facebook.flipper.plugins.uidebugger.util.Immediate @@ -25,6 +27,7 @@ import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred class LayoutTraversal( private val context: UIDContext, ) { + internal val additionalNodeInspectionIds = mutableScatterSetOf() @Suppress("unchecked_cast") private fun NodeDescriptor<*>.asAny(): NodeDescriptor = this as NodeDescriptor @@ -63,7 +66,8 @@ class LayoutTraversal( descriptor.getBounds(node), emptySet(), emptyList(), - null))) + null, + AdditionalDataStatus.NOT_AVAILABLE))) shallow.remove(node) continue @@ -91,24 +95,34 @@ class LayoutTraversal( } } - val attributes = descriptor.getAttributes(node) + val shouldGetAdditionalData = curId in additionalNodeInspectionIds + val attributesInfo = descriptor.getAttributes(node, shouldGetAdditionalData) val bounds = descriptor.getBounds(node) val tags = descriptor.getTags(node) visited.add( - attributes.map { attrs -> + attributesInfo.map { attrsInfo -> + val additionalDataStatus = + if (!attrsInfo.hasAdditionalData) { + AdditionalDataStatus.NOT_AVAILABLE + } else if (shouldGetAdditionalData) { + AdditionalDataStatus.ENABLED + } else { + AdditionalDataStatus.DISABLED + } Node( curId, parentId, descriptor.getQualifiedName(node), descriptor.getName(node), descriptor.getBoxData(node), - attrs, + attrsInfo.attributeSections, descriptor.getInlineAttributes(node), descriptor.getHiddenAttributes(node), bounds, tags, childrenIds, - activeChildId) + activeChildId, + additionalDataStatus) }) } catch (exception: Exception) { Log.e(LogTag, "Error while processing node ${node.javaClass.name} $node", exception) diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/NodeDescriptor.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/NodeDescriptor.kt index a1f2cf7078b..cf6e702fef6 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/NodeDescriptor.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/NodeDescriptor.kt @@ -14,6 +14,7 @@ import com.facebook.flipper.plugins.uidebugger.model.InspectableObject import com.facebook.flipper.plugins.uidebugger.model.Metadata import com.facebook.flipper.plugins.uidebugger.model.MetadataId import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred +import java.lang.IllegalStateException import kotlinx.serialization.json.JsonObject /* @@ -71,9 +72,34 @@ interface NodeDescriptor { /** * Get the attribute to show for this node in the sidebar of the inspector. The object first level * is a section and subsequent objects within are the first level of that section. Nested objects - * will nest in the sidebar + * will nest in the sidebar. + * + * You have to implement only one of [getAttributes(T)] or [getAttributes(T, boolean)] methods. If + * you don't need to support Nodes that can request additional data then use this variant, + * otherwise use [getAttributes(T, boolean)]. */ - fun getAttributes(node: T): MaybeDeferred> + fun getAttributes(node: T): MaybeDeferred> { + throw IllegalStateException( + "One of the getAttributes methods must be implemented by the NodeDescriptor.") + } + + /** + * Get the attribute to show for this node in the sidebar of the inspector. The object first level + * is a section and subsequent objects within are the first level of that section. Nested objects + * will nest in the sidebar. + * + * This is a more advanced version of [getAttributes(T)] that allows for telling Flipper that some + * of the attributes can be computed on demand. When [shouldGetAdditionalData] is true then this + * method should perform the expensive computation on the attributes, if it's false then it + * shouldn't. If any attribute is expensive to compute then this method should return + * [AttributesInfo.hasAdditionalData] set to true which will result in showing some extra UI to be + * in the sidebar of the UI Debugger in Flipper Desktop app. + */ + fun getAttributes(node: T, shouldGetAdditionalData: Boolean): MaybeDeferred { + return getAttributes(node).map { + AttributesInfo(attributeSections = it, hasAdditionalData = false) + } + } /** * Set of tags to describe this node in an abstract way for the UI Unfortunately this can't be an @@ -97,6 +123,11 @@ interface NodeDescriptor { ) {} } +data class AttributesInfo( + val attributeSections: Map, + val hasAdditionalData: Boolean +) + enum class CompoundTypeHint { TOP, LEFT, diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/Node.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/Node.kt index a3a98b4e0d9..ecad357f01d 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/Node.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/Node.kt @@ -25,6 +25,7 @@ data class Node( val tags: Set, val children: List, val activeChild: Id?, + val additionalDataStatus: AdditionalDataStatus, ) /** Expected order is left right top bottom */ @@ -32,3 +33,10 @@ typealias CompactBoxData = List @Serializable class BoxData(val margin: CompactBoxData, val border: CompactBoxData, val padding: CompactBoxData) + +@Serializable +enum class AdditionalDataStatus { + ENABLED, + DISABLED, + NOT_AVAILABLE +} From 1bc7d4d2ad16ab1a6bf2705b4f17a1d0225cb680 Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Thu, 9 May 2024 06:25:16 -0700 Subject: [PATCH 19/92] Small tweaks to custom actions Summary: Made some small adjustments to make things read a bit nicer. the reason for changing initialvalue to value is because flipper desktop uses this same type to keep track of the state as it changes so this saved having to have 2 value fields on the type. Reviewed By: zielinskimz Differential Revision: D57157098 fbshipit-source-id: c11ee399efa3e2f03be04f5104ebe21d54ca9de5 --- .../plugins/jetpackcompose/UIDebuggerComposeSupport.kt | 2 +- .../flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt | 4 ++-- .../flipper/plugins/uidebugger/model/CustomActions.kt | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt index 2b9518d291f..b77d08bb273 100644 --- a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt @@ -51,7 +51,7 @@ object UIDebuggerComposeSupport { private fun addCustomActions(context: UIDContext) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - context.addCustomActionGroup("Compose", ActionIcon.Local("icons/compose-logo.png")) { + context.addCustomActionGroup("Compose options", ActionIcon.Local("icons/compose-logo.png")) { booleanAction("Hide System Nodes", AbstractComposeViewDescriptor.hideSystemNodes) { newValue -> AbstractComposeViewDescriptor.hideSystemNodes = newValue diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt index f2119a5cfcb..c489575ee8e 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/UIDebuggerFlipperPlugin.kt @@ -74,7 +74,7 @@ class UIDebuggerFlipperPlugin(val context: UIDContext) : FlipperPlugin { } } - connection.receive("customActionGroup") { args, responder -> + connection.receive("onCustomAction") { args, responder -> try { val customActionGroupIndex = args.getInt("customActionGroupIndex") val customAction = context.customActionGroups[customActionGroupIndex] @@ -87,7 +87,7 @@ class UIDebuggerFlipperPlugin(val context: UIDContext) : FlipperPlugin { responder.success() } is Action.BooleanAction -> { - val newBooleanActionValue = args.getBoolean("newBooleanActionValue") + val newBooleanActionValue = args.getBoolean("value") val result = item.action(newBooleanActionValue) context.decorViewTracker.requestTraversal() responder.success(FlipperObject.Builder().put("result", result).build()) diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/CustomActions.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/CustomActions.kt index bf97568ed9c..2e72e91aed9 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/CustomActions.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/CustomActions.kt @@ -31,7 +31,7 @@ sealed interface Action { @SerialName("BooleanAction") data class BooleanAction( val title: String, - val initialValue: Boolean, + val value: Boolean, @Transient val action: (Boolean) -> Boolean = { it }, ) : Action } From 70c39209df8134d5b6f131e0661763211687731a Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Fri, 10 May 2024 02:59:10 -0700 Subject: [PATCH 20/92] Replace ScatterSet with a LinkedHashSet Summary: We don't expect to have a lot of elements in this set so using ScatterSet wouldn't make a difference here. And it turns out that OSS version doesn't have a proper dependecy added so using ScatterSet breaks OSS CI build. In this diff I'm replacing it with a regular LinkedHashSet from stdlib. Reviewed By: pengj Differential Revision: D57197303 fbshipit-source-id: e7e9dc9dcab07fcb26b9f26e7fe7a78595ec63a5 --- .../flipper/plugins/uidebugger/core/LayoutTraversal.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt index 437c587ccff..de270e5b486 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt @@ -8,7 +8,6 @@ package com.facebook.flipper.plugins.uidebugger.core import android.util.Log -import androidx.collection.mutableScatterSetOf import com.facebook.flipper.plugins.uidebugger.LogTag import com.facebook.flipper.plugins.uidebugger.descriptors.Id import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor @@ -27,7 +26,7 @@ import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred class LayoutTraversal( private val context: UIDContext, ) { - internal val additionalNodeInspectionIds = mutableScatterSetOf() + internal val additionalNodeInspectionIds = mutableSetOf() @Suppress("unchecked_cast") private fun NodeDescriptor<*>.asAny(): NodeDescriptor = this as NodeDescriptor From 95524a770f43863ac08da5380b8a0c61a781b8a3 Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Fri, 10 May 2024 04:15:56 -0700 Subject: [PATCH 21/92] Implement custom actions api for desktop Summary: For context see this D57046356 Client can define custom action groups which we implement as a dropdown in the tree controls ui. There is boolean type which is a checkbox with state owned by the client There is also unit type where desktop just sends a command The round trip seems fast enough for me to update the checkbox ui after the call comes back from the client, if needed this can be done optimistcailly Changelog: Added compose menu with ability to show / hide system nodes Reviewed By: lblasa Differential Revision: D57157243 fbshipit-source-id: a7d0a2cf49528bd6671e3841a562292e10e46331 --- .../public/ui-debugger/ClientTypes.tsx | 37 +++++ .../components/shared/CustomDropDown.tsx | 17 +++ .../shared/MultiSelectableDropDownItem.tsx | 7 +- .../FrameworkEventsInspector.tsx | 24 ++-- .../components/tree/CustomActions.tsx | 134 ++++++++++++++++++ .../components/tree/TreeControls.tsx | 10 ++ .../components/tree/toTreeList.tsx | 1 + desktop/plugins/public/ui-debugger/index.tsx | 35 +++++ 8 files changed, 252 insertions(+), 13 deletions(-) create mode 100644 desktop/plugins/public/ui-debugger/components/shared/CustomDropDown.tsx create mode 100644 desktop/plugins/public/ui-debugger/components/tree/CustomActions.tsx diff --git a/desktop/plugins/public/ui-debugger/ClientTypes.tsx b/desktop/plugins/public/ui-debugger/ClientTypes.tsx index 0e1d312dac4..1d3c40d11dc 100644 --- a/desktop/plugins/public/ui-debugger/ClientTypes.tsx +++ b/desktop/plugins/public/ui-debugger/ClientTypes.tsx @@ -27,6 +27,11 @@ export type Methods = { metadataIdPath: MetadataId[]; compoundTypeHint?: CompoundTypeHint; }): Promise; + onCustomAction(params: { + customActionGroupIndex: number; + customActionIndex: number; + value: T; + }): Promise<{result: T}>; }; export type CompoundTypeHint = @@ -107,6 +112,38 @@ export type InitEvent = { frameworkEventMetadata?: FrameworkEventMetadata[]; supportedTraversalModes?: TraversalMode[]; currentTraversalMode?: TraversalMode; + customActionGroups?: CustomActionGroup[]; +}; + +type LocalIcon = { + type: 'Local'; + iconPath: string; +}; + +type IconPackIcon = { + type: 'Fb' | 'Antd'; + iconName: string; +}; + +export type BooleanAction = { + type: 'BooleanAction'; + title: string; + value: boolean; +}; + +export type UnitAction = { + type: 'UnitAction'; + title: string; +}; + +export type CustomAction = BooleanAction | UnitAction; + +export type ActionIcon = LocalIcon | IconPackIcon; + +export type CustomActionGroup = { + title: string; + actionIcon: ActionIcon; + actions: CustomAction[]; }; export type PerformanceStatsEvent = { diff --git a/desktop/plugins/public/ui-debugger/components/shared/CustomDropDown.tsx b/desktop/plugins/public/ui-debugger/components/shared/CustomDropDown.tsx new file mode 100644 index 00000000000..7c556002960 --- /dev/null +++ b/desktop/plugins/public/ui-debugger/components/shared/CustomDropDown.tsx @@ -0,0 +1,17 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {Layout, styled} from 'flipper-plugin'; +import {theme} from 'flipper-plugin'; + +export const CustomDropDown = styled(Layout.Container)({ + backgroundColor: theme.white, + borderRadius: theme.borderRadius, + boxShadow: `0 0 4px 1px rgba(0,0,0,0.10)`, +}); diff --git a/desktop/plugins/public/ui-debugger/components/shared/MultiSelectableDropDownItem.tsx b/desktop/plugins/public/ui-debugger/components/shared/MultiSelectableDropDownItem.tsx index e499f89c4b2..e92cf1cd8d5 100644 --- a/desktop/plugins/public/ui-debugger/components/shared/MultiSelectableDropDownItem.tsx +++ b/desktop/plugins/public/ui-debugger/components/shared/MultiSelectableDropDownItem.tsx @@ -26,7 +26,6 @@ export function MultiSelectableDropDownItem({ return ( { e.stopPropagation(); @@ -47,5 +46,9 @@ export const StyledMultiSelectDropDownItem = styled(Layout.Horizontal)({ ':hover': { backgroundColor: theme.backgroundWash, }, - height: 32, + cursor: 'pointer', + height: 38, + userSelect: 'none', + paddingLeft: theme.space.medium, + paddingRight: theme.space.medium, }); diff --git a/desktop/plugins/public/ui-debugger/components/sidebarV2/frameworkevents/FrameworkEventsInspector.tsx b/desktop/plugins/public/ui-debugger/components/sidebarV2/frameworkevents/FrameworkEventsInspector.tsx index e2c20d95406..b2c2a30dc8e 100644 --- a/desktop/plugins/public/ui-debugger/components/sidebarV2/frameworkevents/FrameworkEventsInspector.tsx +++ b/desktop/plugins/public/ui-debugger/components/sidebarV2/frameworkevents/FrameworkEventsInspector.tsx @@ -40,6 +40,7 @@ import {MultiSelectableDropDownItem} from '../../shared/MultiSelectableDropDownI import {formatDuration, formatTimestampMillis} from '../../../utils/timeUtils'; import {tracker} from '../../../utils/tracker'; import {plugin} from '../../../index'; +import {CustomDropDown} from '../../shared/CustomDropDown'; type Props = { node: ClientNode; @@ -122,17 +123,14 @@ export const FrameworkEventsInspector: React.FC = ({ } }} dropdownRender={() => ( - + {showThreadsSection && ( <> - By thread + + By thread + {allThreads.map((thread) => ( { @@ -152,7 +150,11 @@ export const FrameworkEventsInspector: React.FC = ({ {showEventTypesSection && ( <> - By event type + + By event type + {allEventTypes.map((eventType) => ( { @@ -169,7 +171,7 @@ export const FrameworkEventsInspector: React.FC = ({ ))} )} - + )}> + + ); +} diff --git a/desktop/plugins/public/ui-debugger/components/tree/TreeControls.tsx b/desktop/plugins/public/ui-debugger/components/tree/TreeControls.tsx index ab545b95930..12911cf7cc8 100644 --- a/desktop/plugins/public/ui-debugger/components/tree/TreeControls.tsx +++ b/desktop/plugins/public/ui-debugger/components/tree/TreeControls.tsx @@ -40,6 +40,7 @@ import { } from '../shared/FrameworkEventsTreeSelect'; import {createDropDownItem} from '../shared/createDropDownItem'; import {TreeNodeRow} from './Tree'; +import {CustomActionGroupDropDown} from './CustomActions'; type FrameworkEventsDropDownItems = 'OpenTable' | 'Monitoring'; const frameworkEventsDropDownItems = [ @@ -86,6 +87,8 @@ export const TreeControls: React.FC = () => { const isConnected = useValue(instance.uiState.isConnected); + const customActionGroups = useValue(instance.customActionGroups); + return ( { )} + {customActionGroups.map((group, idx) => ( + + ))} {frameworkEventMonitoring.size > 0 && ( <> ) { Map >(new Map()); + const customActionGroups = createState([]); + const uiState: UIState = createUIState(); //this is the client data is what drives all of desktop UI @@ -107,6 +110,10 @@ export function plugin(client: PluginClient) { draft.set(frameworkEventMeta.type, false); }); }); + + if (event.customActionGroups != null) { + customActionGroups.set(event.customActionGroups); + } if ( event.supportedTraversalModes && event.supportedTraversalModes.length > 1 @@ -275,8 +282,36 @@ export function plugin(client: PluginClient) { }); client.onMessage('frameScan', processFrame); + async function onCustomAction( + customActionGroupIndex: number, + customActionIndex: number, + value: T, + ) { + try { + const response = await client.send('onCustomAction', { + customActionGroupIndex, + customActionIndex, + value, + }); + + customActionGroups.update((draft) => { + const action = draft[customActionGroupIndex].actions[customActionIndex]; + + switch (action.type) { + case 'UnitAction': + return; + case 'BooleanAction': + action.value = response.result as boolean; + } + }); + } catch (e) { + console.warn('onCustomAction failed', e); + } + } return { rootId, + customActionGroups, + onCustomAction, currentFrameTime: lastProcessedFrameTime as _ReadOnlyAtom, uiState: uiState as ReadOnlyUIState, uiActions: _uiActions, From 6061dbae40790e0f136391bc596761231395f2ca Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Fri, 10 May 2024 05:59:48 -0700 Subject: [PATCH 22/92] Add complex parameter to Compose sample Summary: As per title Reviewed By: LukeDefeo Differential Revision: D57207794 fbshipit-source-id: b7717e43c09e4e0b52ade4b5cf3d104f4b7001f6 --- .../com/facebook/flipper/sample/JetpackComposeActivity.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/android/sample/src/main/java/com/facebook/flipper/sample/JetpackComposeActivity.kt b/android/sample/src/main/java/com/facebook/flipper/sample/JetpackComposeActivity.kt index 0a8b57941c4..6d588d88ba0 100644 --- a/android/sample/src/main/java/com/facebook/flipper/sample/JetpackComposeActivity.kt +++ b/android/sample/src/main/java/com/facebook/flipper/sample/JetpackComposeActivity.kt @@ -41,7 +41,7 @@ import androidx.compose.ui.unit.sp @Preview @Composable -fun Counter() { +fun Counter(complexParameter: ComplexParameter) { var count: Int by remember { mutableIntStateOf(0) } var openAlertDialog by remember { mutableStateOf(false) } @@ -114,6 +114,10 @@ fun AlertDialogExample( class JetpackComposeActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContent { Counter() } + setContent { Counter(ComplexParameter(ComplexParameterChild("Hello"))) } } } + +class ComplexParameter(val child: ComplexParameterChild) + +class ComplexParameterChild(val value: String) From f64556e9d03adb6e8cdc0ddb2bd22574ff7f8d5d Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Mon, 13 May 2024 06:08:29 -0700 Subject: [PATCH 23/92] Perform reflective parameter inspection for additional inspection nodes Summary: This diff uses the new Flipper API that allows for performing additional computation related to attributes on demand. By default the reflective parameter inspection is turned off which makes Flipper Compose plugin to perform well. User will have an ability to select a specific node in Flipper Desktop UI for which additional computation should be performed. When this happens Flipper Compose plugin will use reflection in order to perform additional analysis only on the selected node. The reflection is still slow but this way the user is in control and if they need to see additional information about some node, they can opt in. This diff brings back `ReflectionScope` class and adds back methods that perform reflective analysis to `ParameterFactory`. Reviewed By: pengj Differential Revision: D57148571 fbshipit-source-id: e53d734b6f9d6c64f69064104765a8089c4f0203 --- .../UIDebuggerComposeSupport.kt | 19 +- .../AbstractComposeViewDescriptor.kt | 19 +- .../descriptors/ComposeNodeDescriptor.kt | 15 +- .../jetpackcompose/model/ComposeNode.kt | 60 +++- .../inspector/LayoutInspectorTree.kt | 8 +- .../ui/inspection/inspector/NodeParameter.kt | 1 + ...arameterFactory.kt => ParameterFactory.kt} | 337 ++++++++++++++++-- .../inspection/inspector/ReflectionScope.kt | 215 +++++++++++ .../uidebugger/core/LayoutTraversal.kt | 14 +- 9 files changed, 626 insertions(+), 62 deletions(-) rename android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/{ReflectionFreeParameterFactory.kt => ParameterFactory.kt} (65%) create mode 100644 android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionScope.kt diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt index b77d08bb273..27ce60fcddd 100644 --- a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt @@ -44,19 +44,22 @@ object UIDebuggerComposeSupport { } private fun addDescriptors(register: DescriptorRegister) { - register.register(AbstractComposeView::class.java, AbstractComposeViewDescriptor) - register.register(ComposeNode::class.java, ComposeNodeDescriptor) - register.register(ComposeInnerViewNode::class.java, ComposeInnerViewDescriptor) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + register.register(AbstractComposeView::class.java, AbstractComposeViewDescriptor) + register.register(ComposeNode::class.java, ComposeNodeDescriptor) + register.register(ComposeInnerViewNode::class.java, ComposeInnerViewDescriptor) + } } private fun addCustomActions(context: UIDContext) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { context.addCustomActionGroup("Compose options", ActionIcon.Local("icons/compose-logo.png")) { - booleanAction("Hide System Nodes", AbstractComposeViewDescriptor.hideSystemNodes) { newValue - -> - AbstractComposeViewDescriptor.hideSystemNodes = newValue - newValue - } + booleanAction( + "Hide System Nodes", AbstractComposeViewDescriptor.layoutInspector.hideSystemNodes) { + newValue -> + AbstractComposeViewDescriptor.layoutInspector.hideSystemNodes = newValue + newValue + } } } } diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/AbstractComposeViewDescriptor.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/AbstractComposeViewDescriptor.kt index edce98ef544..ea442aa8f88 100644 --- a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/AbstractComposeViewDescriptor.kt +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/AbstractComposeViewDescriptor.kt @@ -22,19 +22,17 @@ import facebook.internal.androidx.compose.ui.inspection.inspector.InspectorNode import facebook.internal.androidx.compose.ui.inspection.inspector.LayoutInspectorTree import java.io.IOException -@RequiresApi(Build.VERSION_CODES.Q) object AbstractComposeViewDescriptor : ChainedDescriptor() { - internal var hideSystemNodes: Boolean = true + @RequiresApi(Build.VERSION_CODES.Q) internal val layoutInspector = LayoutInspectorTree() - private val recompositionHandler by lazy { - RecompositionHandler(DefaultArtTooling("Flipper")).apply { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - attachJvmtiAgent() - startTrackingRecompositions(this) + private val recompositionHandler = + RecompositionHandler(DefaultArtTooling("Flipper")).apply { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + attachJvmtiAgent() + startTrackingRecompositions(this) + } } - } - } override fun onGetName(node: AbstractComposeView): String = node.javaClass.simpleName @@ -69,8 +67,7 @@ object AbstractComposeViewDescriptor : ChainedDescriptor() children.add(child) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - val layoutInspector = LayoutInspectorTree() - layoutInspector.hideSystemNodes = hideSystemNodes + layoutInspector.resetAccumulativeState() val composeNodes = try { transform(child, layoutInspector.convert(child), layoutInspector) diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeNodeDescriptor.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeNodeDescriptor.kt index 25a7d807886..109209d33a9 100644 --- a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeNodeDescriptor.kt +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/ComposeNodeDescriptor.kt @@ -12,6 +12,7 @@ import androidx.annotation.RequiresApi import com.facebook.flipper.plugins.jetpackcompose.JetpackComposeTag import com.facebook.flipper.plugins.jetpackcompose.descriptors.ComposeNodeDescriptor.toInspectableValue import com.facebook.flipper.plugins.jetpackcompose.model.ComposeNode +import com.facebook.flipper.plugins.uidebugger.descriptors.AttributesInfo import com.facebook.flipper.plugins.uidebugger.descriptors.BaseTags import com.facebook.flipper.plugins.uidebugger.descriptors.Id import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister @@ -78,7 +79,10 @@ object ComposeNodeDescriptor : NodeDescriptor { return node.children } - override fun getAttributes(node: ComposeNode): MaybeDeferred> { + override fun getAttributes( + node: ComposeNode, + shouldGetAdditionalData: Boolean + ): MaybeDeferred { return Deferred { val builder = mutableMapOf() val props = mutableMapOf() @@ -115,26 +119,26 @@ object ComposeNodeDescriptor : NodeDescriptor { } val params = mutableMapOf() - node.parameters.forEach { parameter -> + node.getParameters(shouldGetAdditionalData).forEach { parameter -> fillNodeParameters(parameter, params, node.inspectorNode.name) } builder[ParametersAttributeId] = InspectableObject(params.toMap()) val mergedSemantics = mutableMapOf() - node.mergedSemantics.forEach { parameter -> + node.getMergedSemantics(shouldGetAdditionalData).forEach { parameter -> fillNodeParameters(parameter, mergedSemantics, node.inspectorNode.name) } builder[MergedSemanticsAttributeId] = InspectableObject(mergedSemantics.toMap()) val unmergedSemantics = mutableMapOf() - node.unmergedSemantics.forEach { parameter -> + node.getUnmergedSemantics(shouldGetAdditionalData).forEach { parameter -> fillNodeParameters(parameter, unmergedSemantics, node.inspectorNode.name) } builder[UnmergedSemanticsAttributeId] = InspectableObject(unmergedSemantics.toMap()) builder[SectionId] = InspectableObject(props.toMap()) - builder + AttributesInfo(builder, node.hasAdditionalData) } } @@ -189,6 +193,7 @@ object ComposeNodeDescriptor : NodeDescriptor { private fun NodeParameter.toInspectableValue(): InspectableValue { return when (type) { ParameterType.Iterable, + ParameterType.ComplexObject, ParameterType.String -> InspectableValue.Text(value.toString()) ParameterType.Boolean -> InspectableValue.Boolean(value as Boolean) ParameterType.Int32 -> InspectableValue.Number(value as Int) diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeNode.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeNode.kt index 8a3e7c0d40a..61fc44bb729 100644 --- a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeNode.kt +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/model/ComposeNode.kt @@ -18,6 +18,7 @@ import facebook.internal.androidx.compose.ui.inspection.inspector.InspectorNode import facebook.internal.androidx.compose.ui.inspection.inspector.LayoutInspectorTree import facebook.internal.androidx.compose.ui.inspection.inspector.NodeParameter import facebook.internal.androidx.compose.ui.inspection.inspector.ParameterKind +import facebook.internal.androidx.compose.ui.inspection.inspector.ParameterType // Same values as in AndroidX (ComposeLayoutInspector.kt) private const val MAX_RECURSIONS = 2 @@ -47,21 +48,51 @@ class ComposeNode( val children: List = collectChildren() - val parameters: List by lazy { getNodeParameters(ParameterKind.Normal) } + val hasAdditionalData: Boolean + get() { + return hasAdditionalParameterData || + hasAdditionalMergedSemanticsData || + hasAdditionalUnmergedSemanticsData + } + + private var hasAdditionalParameterData: Boolean = false + private var hasAdditionalMergedSemanticsData: Boolean = false + private var hasAdditionalUnmergedSemanticsData: Boolean = false - val mergedSemantics: List by lazy { - getNodeParameters(ParameterKind.MergedSemantics) + fun getParameters(useReflection: Boolean): List { + return getNodeParameters(ParameterKind.Normal, useReflection) } - val unmergedSemantics: List by lazy { - getNodeParameters(ParameterKind.UnmergedSemantics) + fun getMergedSemantics(useReflection: Boolean): List { + return getNodeParameters(ParameterKind.MergedSemantics, useReflection) } - private fun getNodeParameters(kind: ParameterKind): List { + fun getUnmergedSemantics(useReflection: Boolean): List { + return getNodeParameters(ParameterKind.UnmergedSemantics, useReflection) + } + + private fun getNodeParameters(kind: ParameterKind, useReflection: Boolean): List { layoutInspectorTree.resetAccumulativeState() return try { - layoutInspectorTree.convertParameters( - inspectorNode.id, inspectorNode, kind, MAX_RECURSIONS, MAX_ITERABLE_SIZE) + val params = + layoutInspectorTree.convertParameters( + inspectorNode.id, + inspectorNode, + kind, + MAX_RECURSIONS, + MAX_ITERABLE_SIZE, + useReflection) + if (!useReflection) { + // We only need to check for additional data if we are not using reflection since + // params parsed with useReflection == true wont have complex objects + val hasAdditionalData = hasAdditionalData(params) + when (kind) { + ParameterKind.Normal -> hasAdditionalParameterData = hasAdditionalData + ParameterKind.MergedSemantics -> hasAdditionalMergedSemanticsData = hasAdditionalData + ParameterKind.UnmergedSemantics -> hasAdditionalUnmergedSemanticsData = hasAdditionalData + } + } + params } catch (t: Throwable) { Log.e(TAG, "Failed to get parameters.", t) emptyList() @@ -107,6 +138,19 @@ class ComposeNode( return null } + private fun hasAdditionalData(params: List): Boolean { + val queue = ArrayDeque() + queue.addAll(params) + while (!queue.isEmpty()) { + val param = queue.removeFirst() + if (param.type == ParameterType.ComplexObject) { + return true + } + queue.addAll(param.elements) + } + return false + } + companion object { private const val TAG = "ComposeNode" } diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt index 48eca52c231..8d4736f38b4 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/LayoutInspectorTree.kt @@ -89,7 +89,7 @@ class LayoutInspectorTree { private var foundNode: InspectorNode? = null private var windowSize = emptySize private val inlineClassConverter = InlineClassConverter() - private val parameterFactory = ReflectionFreeParameterFactory() + private val parameterFactory = ParameterFactory(inlineClassConverter) private val cache = ArrayDeque() private var generatedId = -1L private val subCompositions = SubCompositionRoots() @@ -168,7 +168,8 @@ class LayoutInspectorTree { node: InspectorNode, kind: ParameterKind, maxRecursions: Int, - maxInitialIterableSize: Int + maxInitialIterableSize: Int, + useReflection: Boolean ): List { val parameters = node.parametersByKind(kind) return parameters.mapIndexed { index, parameter -> @@ -181,7 +182,8 @@ class LayoutInspectorTree { kind, index, maxRecursions, - maxInitialIterableSize) + maxInitialIterableSize, + useReflection) } } diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/NodeParameter.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/NodeParameter.kt index b2a3ce1a040..e4807083e7c 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/NodeParameter.kt +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/NodeParameter.kt @@ -54,4 +54,5 @@ enum class ParameterType { Lambda, FunctionReference, Iterable, + ComplexObject, } diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionFreeParameterFactory.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ParameterFactory.kt similarity index 65% rename from android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionFreeParameterFactory.kt rename to android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ParameterFactory.kt index a996d8b5fe9..356bab0d4be 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionFreeParameterFactory.kt +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ParameterFactory.kt @@ -16,13 +16,17 @@ package facebook.internal.androidx.compose.ui.inspection.inspector +import android.util.Log import android.view.View import androidx.collection.mutableIntListOf import androidx.collection.mutableLongObjectMapOf import androidx.compose.runtime.internal.ComposableLambda +import androidx.compose.ui.AbsoluteAlignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.Shadow import androidx.compose.ui.graphics.SolidColor import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.vector.ImageVector @@ -36,6 +40,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.ResourceFont import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.text.style.BaselineShift +import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.TextUnit @@ -43,24 +48,71 @@ import androidx.compose.ui.unit.TextUnitType import facebook.internal.androidx.compose.ui.inspection.inspector.ParameterType.DimensionDp import facebook.internal.androidx.compose.ui.inspection.util.copy import facebook.internal.androidx.compose.ui.inspection.util.removeLast +import java.lang.reflect.Field +import java.lang.reflect.Modifier as JavaModifier import java.util.IdentityHashMap import kotlin.jvm.internal.FunctionReference import kotlin.jvm.internal.Lambda import kotlin.math.abs +import kotlin.reflect.KClass +import kotlin.reflect.KProperty +import kotlin.reflect.KProperty1 +import kotlin.reflect.full.allSuperclasses +import kotlin.reflect.full.declaredMemberProperties +import kotlin.reflect.jvm.isAccessible +import kotlin.reflect.jvm.javaField +import kotlin.reflect.jvm.javaGetter /** * Factory of [NodeParameter]s. * * Each parameter value is converted to a user readable value. - * - * Note: This file has been modified so that it doesn't rely on Kotlin reflection API. */ -internal class ReflectionFreeParameterFactory { +internal class ParameterFactory(private val inlineClassConverter: InlineClassConverter) { + + private val reflectionScope: ReflectionScope = ReflectionScope() + + /** A map from known values to a user readable string representation. */ + private val valueLookup = mutableMapOf() + /** The classes we have loaded constants from. */ + private val valuesLoaded = mutableSetOf>() + + /** + * Do not load constant names from instances of these classes. We prefer showing the raw values of + * Color and Dimensions. + */ + private val ignoredClasses = listOf(Color::class.java, Dp::class.java) private var creatorCache: ParameterCreator? = null + /** + * Do not decompose instances or lookup constants from these package prefixes + * + * The following instances are known to contain self recursion: + * - kotlinx.coroutines.flow.StateFlowImpl + * - androidx.compose.ui.node.LayoutNode + */ + private val ignoredPackagePrefixes = + listOf("android.", "java.", "javax.", "kotlinx.", "androidx.compose.ui.node.") + var density = Density(1.0f) + init { + val textDecorationCombination = + TextDecoration.combine(listOf(TextDecoration.LineThrough, TextDecoration.Underline)) + valueLookup[textDecorationCombination] = "LineThrough+Underline" + valueLookup[Color.Unspecified] = "Unspecified" + valueLookup[RectangleShape] = "RectangleShape" + valuesLoaded.add(Enum::class.java) + valuesLoaded.add(Any::class.java) + + // AbsoluteAlignment is not found from an instance of BiasAbsoluteAlignment, + // because Alignment has no file level class. + reflectionScope.withReflectiveAccess { + loadConstantsFromEnclosedClasses(AbsoluteAlignment::class.java) + } + } + /** * Create a [NodeParameter] from the specified parameter [name] and [value]. * @@ -76,30 +128,169 @@ internal class ReflectionFreeParameterFactory { kind: ParameterKind, parameterIndex: Int, maxRecursions: Int, - maxInitialIterableSize: Int + maxInitialIterableSize: Int, + useReflection: Boolean ): NodeParameter { val creator = creatorCache ?: ParameterCreator() try { - return creator.create( - rootId, - nodeId, - anchorId, - name, - value, - kind, - parameterIndex, - maxRecursions, - maxInitialIterableSize) + return if (useReflection) { + reflectionScope.withReflectiveAccess { + creator.create( + rootId, + nodeId, + anchorId, + name, + value, + kind, + parameterIndex, + maxRecursions, + maxInitialIterableSize, + true) + } + } else { + creator.create( + rootId, + nodeId, + anchorId, + name, + value, + kind, + parameterIndex, + maxRecursions, + maxInitialIterableSize, + false) + } } finally { creatorCache = creator } } fun clearReferenceCache() { - val creator = creatorCache ?: return - creator.clearReferenceCache() + creatorCache?.clearReferenceCache() + } + + private fun loadConstantsFrom(javaClass: Class<*>) { + if (valuesLoaded.contains(javaClass) || + ignoredPackagePrefixes.any { javaClass.name.startsWith(it) }) { + return + } + val related = generateSequence(javaClass) { it.superclass }.plus(javaClass.interfaces) + related.forEach { aClass -> + val topClass = generateSequence(aClass) { safeEnclosingClass(it) }.last() + loadConstantsFromEnclosedClasses(topClass) + findPackageLevelClass(topClass)?.let { loadConstantsFromStaticFinal(it) } + } + } + + private fun safeEnclosingClass(klass: Class<*>): Class<*>? = + try { + klass.enclosingClass + } catch (_: Error) { + // Exceptions seen on API 23... + null + } + + private fun findPackageLevelClass(javaClass: Class<*>): Class<*>? = + try { + // Note: This doesn't work when @file.JvmName is specified + Class.forName("${javaClass.name}Kt") + } catch (ex: Throwable) { + null + } + + private fun loadConstantsFromEnclosedClasses(javaClass: Class<*>) { + if (valuesLoaded.contains(javaClass)) { + return + } + loadConstantsFromObjectInstance(javaClass.kotlin) + loadConstantsFromStaticFinal(javaClass) + valuesLoaded.add(javaClass) + javaClass.declaredClasses.forEach { loadConstantsFromEnclosedClasses(it) } } + /** + * Load all constants from companion objects and singletons + * + * Exclude: primary types and types of ignoredClasses, open and lateinit vals. + */ + private fun loadConstantsFromObjectInstance(kClass: KClass<*>) { + try { + val instance = kClass.objectInstance ?: return + kClass.declaredMemberProperties + .asSequence() + .filter { it.isFinal && !it.isLateinit } + .mapNotNull { constantValueOf(it, instance)?.let { key -> Pair(key, it.name) } } + .filter { !ignoredValue(it.first) } + .toMap(valueLookup) + } catch (_: Throwable) { + // KT-16479 : kotlin reflection does currently not support packages and files. + // We load top level values using Java reflection instead. + // Ignore other reflection errors as well + } + } + + /** + * Load all constants from top level values from Java. + * + * Exclude: primary types and types of ignoredClasses. Since this is Java, inline types will also + * (unfortunately) be excluded. + */ + private fun loadConstantsFromStaticFinal(javaClass: Class<*>) { + try { + javaClass.declaredMethods + .asSequence() + .filter { + it.returnType != Void.TYPE && + JavaModifier.isStatic(it.modifiers) && + JavaModifier.isFinal(it.modifiers) && + !it.returnType.isPrimitive && + it.parameterTypes.isEmpty() && + it.name.startsWith("get") + } + .mapNotNull { javaClass.getDeclaredField(it.name.substring(3)) } + .mapNotNull { constantValueOf(it)?.let { key -> Pair(key, it.name) } } + .filter { !ignoredValue(it.first) } + .toMap(valueLookup) + } catch (_: ReflectiveOperationException) { + // ignore reflection errors + } catch (_: NoClassDefFoundError) { + // ignore missing classes on lower level SDKs + } + } + + private fun constantValueOf(field: Field?): Any? = + try { + field?.isAccessible = true + field?.get(null) + } catch (_: ReflectiveOperationException) { + // ignore reflection errors + null + } + + private fun constantValueOf(property: KProperty1, instance: Any): Any? = + try { + val field = property.javaField + field?.isAccessible = true + inlineClassConverter.castParameterValue(inlineResultClass(property), field?.get(instance)) + } catch (_: ReflectiveOperationException) { + // ignore reflection errors + null + } + + private fun inlineResultClass(property: KProperty1): String? { + // The Java getter name will be mangled if it contains parameters of an inline class. + // The mangled part starts with a '-'. + if (property.javaGetter?.name?.contains('-') == true) { + return property.returnType.toString() + } + return null + } + + private fun ignoredValue(value: Any?): Boolean = + value == null || + ignoredClasses.any { ignored -> ignored.isInstance(value) } || + value::class.java.isPrimitive + /** Convenience class for building [NodeParameter]s. */ private inner class ParameterCreator { private var rootId = 0L @@ -115,6 +306,7 @@ internal class ReflectionFreeParameterFactory { private val rootValueIndexCache = mutableLongObjectMapOf>() private var valueIndexMap = IdentityHashMap() + private var useReflection = false fun create( rootId: Long, @@ -125,11 +317,19 @@ internal class ReflectionFreeParameterFactory { kind: ParameterKind, parameterIndex: Int, maxRecursions: Int, - maxInitialIterableSize: Int + maxInitialIterableSize: Int, + useReflection: Boolean ): NodeParameter = try { setup( - rootId, nodeId, anchorId, kind, parameterIndex, maxRecursions, maxInitialIterableSize) + rootId, + nodeId, + anchorId, + kind, + parameterIndex, + maxRecursions, + maxInitialIterableSize, + useReflection) create(name, value, null) ?: createEmptyParameter(name) } finally { setup() @@ -146,7 +346,8 @@ internal class ReflectionFreeParameterFactory { newKind: ParameterKind = ParameterKind.Normal, newParameterIndex: Int = 0, maxRecursions: Int = 0, - maxInitialIterableSize: Int = 0 + maxInitialIterableSize: Int = 0, + useReflection: Boolean = false ) { rootId = newRootId nodeId = newNodeId @@ -159,6 +360,7 @@ internal class ReflectionFreeParameterFactory { valueIndex.clear() valueLazyReferenceMap.clear() valueIndexMap = rootValueIndexCache.getOrPut(newRootId) { IdentityHashMap() } + this.useReflection = useReflection } private fun create(name: String, value: Any?, parentValue: Any?): NodeParameter? { @@ -188,7 +390,11 @@ internal class ReflectionFreeParameterFactory { if (value == null) { return null } - + if (useReflection) { + createFromConstant(name, value)?.let { + return it + } + } return when (value) { is AnnotatedString -> NodeParameter(name, ParameterType.String, value.text) is BaselineShift -> createFromBaselineShift(name, value) @@ -234,8 +440,21 @@ internal class ReflectionFreeParameterFactory { createFromSequence(name, value, value.asSequence(), startIndex, maxElements) value.javaClass.isArray -> createFromArray(name, value, startIndex, maxElements) value is Offset -> createFromOffset(name, value) + value is Shadow -> { + if (useReflection) { + createFromShadow(name, value) + } else { + NodeParameter(name, ParameterType.ComplexObject, value.toString()) + } + } value is TextStyle -> createFromTextStyle(name, value) - else -> NodeParameter(name, ParameterType.String, value.toString()) + else -> { + if (useReflection) { + createFromKotlinReflection(name, value) + } else { + NodeParameter(name, ParameterType.ComplexObject, value.toString()) + } + } } private fun createRecursively( @@ -382,6 +601,11 @@ internal class ReflectionFreeParameterFactory { null } + private fun createFromConstant(name: String, value: Any): NodeParameter? { + loadConstantsFrom(value.javaClass) + return valueLookup[value]?.let { NodeParameter(name, ParameterType.String, it) } + } + // For now: select ResourceFontFont closest to W400 and Normal, and return the resId private fun createFromFontListFamily(name: String, value: FontListFontFamily): NodeParameter? = findBestResourceFont(value)?.let { NodeParameter(name, ParameterType.Resource, it.resId) } @@ -389,6 +613,63 @@ internal class ReflectionFreeParameterFactory { private fun createFromFunctionReference(name: String, value: FunctionReference): NodeParameter = NodeParameter(name, ParameterType.FunctionReference, arrayOf(value, value.name)) + private fun createFromKotlinReflection(name: String, value: Any): NodeParameter? { + val simpleName = value::class.simpleName + val properties = lookup(value) ?: return null + val parameter = NodeParameter(name, ParameterType.String, simpleName) + return when { + properties.isEmpty() -> parameter + !shouldRecurseDeeper() -> parameter.withChildReference(value) + else -> { + val elements = parameter.store(value).elements + properties.values.mapIndexedNotNullTo(elements) { index, part -> + createRecursively(part.name, valueOf(part, value), value, index) + } + parameter.removeIfEmpty(value) + } + } + } + + private fun lookup(value: Any): Map>? { + val kClass = value::class + val simpleName = kClass.simpleName + val qualifiedName = kClass.qualifiedName + if (simpleName == null || + qualifiedName == null || + ignoredPackagePrefixes.any { qualifiedName.startsWith(it) } || + kClass.allSuperclasses.any { superClass -> + val superClassQualifiedName = superClass.qualifiedName + superClassQualifiedName == null || + ignoredPackagePrefixes.any { superClassQualifiedName.startsWith(it) } + }) { + // Exit without creating a parameter for: + // - internal synthetic classes + // - certain android packages + return null + } + return try { + sequenceOf(kClass) + .plus(kClass.allSuperclasses.asSequence()) + .flatMap { it.declaredMemberProperties.asSequence() } + .associateBy { it.name } + } catch (ex: Throwable) { + Log.w("Compose", "Could not decompose ${kClass.simpleName}", ex) + null + } + } + + private fun valueOf(property: KProperty<*>, instance: Any): Any? = + try { + property.isAccessible = true + // Bug in kotlin reflection API: if the type is a nullable inline type with a null + // value, we get an IllegalArgumentException in this line: + property.getter.call(instance) + } catch (ex: Throwable) { + // TODO: Remove this warning since this is expected with nullable inline types + Log.w("Compose", "Could not get value of ${property.name}") + null + } + private fun createFromInspectableValue(name: String, value: InspectableValue): NodeParameter { val tempValue = value.valueOverride ?: "" val parameterName = name.ifEmpty { value.nameFallback } ?: "element" @@ -510,6 +791,20 @@ internal class ReflectionFreeParameterFactory { return parameter } + // Special handling of blurRadius: convert to dp: + private fun createFromShadow(name: String, value: Shadow): NodeParameter? { + val parameter = createFromKotlinReflection(name, value) ?: return null + val elements = parameter.elements + val index = elements.indexOfFirst { it.name == "blurRadius" } + if (index >= 0) { + val existing = elements[index] + val blurRadius = with(density) { value.blurRadius.toDp().value } + elements[index] = NodeParameter("blurRadius", DimensionDp, blurRadius) + elements[index].index = existing.index + } + return parameter + } + // Temporary handling of TextStyle: remove when TextStyle implements InspectableValue // Hide: paragraphStyle, spanStyle, platformStyle, lineHeightStyle private fun createFromTextStyle(name: String, value: TextStyle): NodeParameter? { diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionScope.kt b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionScope.kt new file mode 100644 index 00000000000..321d9958d94 --- /dev/null +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/ReflectionScope.kt @@ -0,0 +1,215 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package facebook.internal.androidx.compose.ui.inspection.inspector + +import android.annotation.SuppressLint +import java.lang.reflect.Field +import java.lang.reflect.Modifier +import kotlin.jvm.internal.FunctionBase +import kotlin.jvm.internal.FunctionReference +import kotlin.jvm.internal.Lambda +import kotlin.jvm.internal.MutablePropertyReference0 +import kotlin.jvm.internal.MutablePropertyReference1 +import kotlin.jvm.internal.MutablePropertyReference2 +import kotlin.jvm.internal.PropertyReference0 +import kotlin.jvm.internal.PropertyReference1 +import kotlin.jvm.internal.PropertyReference2 +import kotlin.jvm.internal.Reflection +import kotlin.jvm.internal.ReflectionFactory +import kotlin.reflect.KClass +import kotlin.reflect.KClassifier +import kotlin.reflect.KDeclarationContainer +import kotlin.reflect.KFunction +import kotlin.reflect.KMutableProperty0 +import kotlin.reflect.KMutableProperty1 +import kotlin.reflect.KMutableProperty2 +import kotlin.reflect.KProperty0 +import kotlin.reflect.KProperty1 +import kotlin.reflect.KProperty2 +import kotlin.reflect.KType +import kotlin.reflect.KTypeParameter +import kotlin.reflect.KTypeProjection +import kotlin.reflect.KVariance +import kotlin.reflect.jvm.internal.ReflectionFactoryImpl + +/** + * Scope that allows to use jarjar-ed kotlin-reflect artifact that is shipped with inspector itself. + * + * Issue with kotlin-reflect. Many of reflective calls such as "foo::class" rely on static functions + * defined in kotlin-stdlib's Reflection.java that delegate to ReflectionFactory. In order to + * initialize that factory kotlin-stdlib statically detects presence or absence of kotlin-reflect in + * classloader and chooses a factory accordingly. If there is no kotlin-reflect, very limited + * version of ReflectionFactory is used. + * + * It is an issue for inspectors because they could be loaded after that factory is initialised, and + * even if they are loaded before, they live in a separate child classloader, thus kotlin-reflect in + * inspector wouldn't exist for kotlin-stdlib in app. + * + * First step to avoid the issue is using ReflectionFactoryImpl that is bundled with inspector. Code + * for that would be fairly simple, for example instead of directly calling + * `kClass.declaredMemberProperties`, correct instance of kClass should be obtained from factory: + * `factory.getOrCreateKotlinClass(kClass.java).declaredMemberProperties`. + * + * That would work if code that works with correct KClass full implementation would never try to + * access a default factory installed in Reflection.java. Unfortunately it is not true, it + * eventually calls `CallableReference.getOwner()` in stdlib that uses default factory. + * + * As a result we have to replace the factory in Reflection.java. To avoid issues with user's code + * factory that we setup is smart, by default it simply delegates to a factory that was previously + * installed. Only within `reflectionScope.withReflectiveAccess{ }` factory from kotlin-reflect is + * used. + */ +@SuppressLint("BanUncheckedReflection") +class ReflectionScope { + + companion object { + init { + allowHiddenApi() + } + } + + private val scopedReflectionFactory = installScopedReflectionFactory() + + /** Runs `block` with access to kotlin-reflect. */ + fun withReflectiveAccess(block: () -> T): T { + return scopedReflectionFactory.withMainFactory(block) + } + + private fun installScopedReflectionFactory(): ScopedReflectionFactory { + val factoryField = Reflection::class.java.getDeclaredField("factory") + factoryField.isAccessible = true + val original: ReflectionFactory = factoryField.get(null) as ReflectionFactory + val modifiersField: Field = Field::class.java.getDeclaredField("accessFlags") + modifiersField.isAccessible = true + // make field non-final 😅 b/179685774 https://youtrack.jetbrains.com/issue/KT-44795 + modifiersField.setInt(factoryField, factoryField.modifiers and Modifier.FINAL.inv()) + val scopedReflectionFactory = ScopedReflectionFactory(original) + factoryField.set(null, scopedReflectionFactory) + return scopedReflectionFactory + } +} + +@SuppressLint("BanUncheckedReflection") +private fun allowHiddenApi() { + try { + val vmDebug = Class.forName("dalvik.system.VMDebug") + val allowHiddenApiReflectionFrom = + vmDebug.getDeclaredMethod("allowHiddenApiReflectionFrom", Class::class.java) + allowHiddenApiReflectionFrom.invoke(null, ReflectionScope::class.java) + } catch (e: Throwable) { + // ignore failure, let's try to proceed without it + } +} + +private class ScopedReflectionFactory( + private val original: ReflectionFactory, +) : ReflectionFactory() { + private val mainFactory = ReflectionFactoryImpl() + private val threadLocalFactory = ThreadLocal() + + fun withMainFactory(block: () -> T): T { + threadLocalFactory.set(mainFactory) + try { + return block() + } finally { + threadLocalFactory.set(null) + } + } + + val factory: ReflectionFactory + get() = threadLocalFactory.get() ?: original + + override fun createKotlinClass(javaClass: Class<*>?): KClass<*> { + return factory.createKotlinClass(javaClass) + } + + override fun createKotlinClass(javaClass: Class<*>?, internalName: String?): KClass<*> { + return factory.createKotlinClass(javaClass, internalName) + } + + override fun getOrCreateKotlinPackage( + javaClass: Class<*>?, + moduleName: String? + ): KDeclarationContainer { + return factory.getOrCreateKotlinPackage(javaClass, moduleName) + } + + override fun getOrCreateKotlinClass(javaClass: Class<*>?): KClass<*> { + return factory.getOrCreateKotlinClass(javaClass) + } + + override fun getOrCreateKotlinClass(javaClass: Class<*>?, internalName: String?): KClass<*> { + return factory.getOrCreateKotlinClass(javaClass, internalName) + } + + override fun renderLambdaToString(lambda: Lambda<*>?): String { + return factory.renderLambdaToString(lambda) + } + + override fun renderLambdaToString(lambda: FunctionBase<*>?): String { + return factory.renderLambdaToString(lambda) + } + + override fun function(f: FunctionReference?): KFunction<*> { + return factory.function(f) + } + + override fun property0(p: PropertyReference0?): KProperty0<*> { + return factory.property0(p) + } + + override fun mutableProperty0(p: MutablePropertyReference0?): KMutableProperty0<*> { + return factory.mutableProperty0(p) + } + + override fun property1(p: PropertyReference1?): KProperty1<*, *> { + return factory.property1(p) + } + + override fun mutableProperty1(p: MutablePropertyReference1?): KMutableProperty1<*, *> { + return factory.mutableProperty1(p) + } + + override fun property2(p: PropertyReference2?): KProperty2<*, *, *> { + return factory.property2(p) + } + + override fun mutableProperty2(p: MutablePropertyReference2?): KMutableProperty2<*, *, *> { + return factory.mutableProperty2(p) + } + + override fun typeOf( + klass: KClassifier?, + arguments: MutableList?, + isMarkedNullable: Boolean + ): KType { + return factory.typeOf(klass, arguments, isMarkedNullable) + } + + override fun typeParameter( + container: Any?, + name: String?, + variance: KVariance?, + isReified: Boolean + ): KTypeParameter { + return factory.typeParameter(container, name, variance, isReified) + } + + override fun setUpperBounds(typeParameter: KTypeParameter?, bounds: MutableList?) { + factory.setUpperBounds(typeParameter, bounds) + } +} diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt index de270e5b486..3b94fc7f77e 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt @@ -101,12 +101,14 @@ class LayoutTraversal( visited.add( attributesInfo.map { attrsInfo -> val additionalDataStatus = - if (!attrsInfo.hasAdditionalData) { - AdditionalDataStatus.NOT_AVAILABLE - } else if (shouldGetAdditionalData) { - AdditionalDataStatus.ENABLED - } else { - AdditionalDataStatus.DISABLED + when (!shouldGetAdditionalData && !attrsInfo.hasAdditionalData) { + true -> AdditionalDataStatus.NOT_AVAILABLE + false -> { + when (shouldGetAdditionalData) { + true -> AdditionalDataStatus.ENABLED + false -> AdditionalDataStatus.DISABLED + } + } } Node( curId, From e2c826ddd23c669d9014a32d32d806467a234b9d Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Mon, 13 May 2024 07:00:26 -0700 Subject: [PATCH 24/92] Upgrade dummy devices Summary: Dummy devices are not "real devices" but rather devices that are created in the absence of them. Sometimes, when trying to find a real device which a specific serial, it will fail, and a dummy device with said serial will be created instead. Later on, it is possible for the real device to become available. In this case, it should be possible to promote the dummy device to an actual device. Reviewed By: passy Differential Revision: D57163208 fbshipit-source-id: 0d42d586ba767f5bee37af9a9d4c52b7518d4adc --- desktop/flipper-server/src/FlipperServerImpl.tsx | 5 ++++- desktop/flipper-ui/src/dispatcher/flipperServer.tsx | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/desktop/flipper-server/src/FlipperServerImpl.tsx b/desktop/flipper-server/src/FlipperServerImpl.tsx index 45d25b7a185..98f7374d3cc 100644 --- a/desktop/flipper-server/src/FlipperServerImpl.tsx +++ b/desktop/flipper-server/src/FlipperServerImpl.tsx @@ -685,7 +685,10 @@ export class FlipperServerImpl implements FlipperServer { const existing = this.devices.get(serial); if (existing) { // assert different kind of devices aren't accidentally reusing the same serial - if (Object.getPrototypeOf(existing) !== Object.getPrototypeOf(device)) { + if ( + existing.info.deviceType !== 'dummy' && + Object.getPrototypeOf(existing) !== Object.getPrototypeOf(device) + ) { throw new Error( `Tried to register a new device type for existing serial '${serial}': Trying to replace existing '${ Object.getPrototypeOf(existing).constructor.name diff --git a/desktop/flipper-ui/src/dispatcher/flipperServer.tsx b/desktop/flipper-ui/src/dispatcher/flipperServer.tsx index 5d4950067d9..8118cd6d23a 100644 --- a/desktop/flipper-ui/src/dispatcher/flipperServer.tsx +++ b/desktop/flipper-ui/src/dispatcher/flipperServer.tsx @@ -361,8 +361,11 @@ export function handleDeviceConnected( `Tried to replace still connected device '${existing.serial}' with a new instance.`, ); } - if (store.getState().settingsState.persistDeviceData) { - //Recycle device + if ( + existing.deviceType !== 'dummy' && + store.getState().settingsState.persistDeviceData + ) { + // Recycle device existing?.connected.set(true); store.dispatch({ type: 'SELECT_DEVICE', From d12cdf332507dffe84af3b0b59c30239d7fec97e Mon Sep 17 00:00:00 2001 From: Michal Zielinski Date: Wed, 15 May 2024 04:49:53 -0700 Subject: [PATCH 25/92] Add action that resets recomposition counts Summary: As per title - this diff adds a custom action which allows for resetting recomposition counts. Reviewed By: luluwu2032 Differential Revision: D57371575 fbshipit-source-id: 81608bd6e6430c6dd28421c385746b98dee9e6d8 --- .../plugins/jetpackcompose/UIDebuggerComposeSupport.kt | 3 +++ .../descriptors/AbstractComposeViewDescriptor.kt | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt index 27ce60fcddd..01774031fe8 100644 --- a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/UIDebuggerComposeSupport.kt @@ -60,6 +60,9 @@ object UIDebuggerComposeSupport { AbstractComposeViewDescriptor.layoutInspector.hideSystemNodes = newValue newValue } + unitAction("Reset Recomposition Counts", ActionIcon.Antd("CloseSquareOutlined")) { + AbstractComposeViewDescriptor.resetRecompositionCounts() + } } } } diff --git a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/AbstractComposeViewDescriptor.kt b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/AbstractComposeViewDescriptor.kt index ea442aa8f88..2c8e41800fd 100644 --- a/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/AbstractComposeViewDescriptor.kt +++ b/android/plugins/jetpack-compose/src/main/java/com/facebook/flipper/plugins/jetpackcompose/descriptors/AbstractComposeViewDescriptor.kt @@ -34,6 +34,10 @@ object AbstractComposeViewDescriptor : ChainedDescriptor() } } + fun resetRecompositionCounts() { + recompositionHandler.changeCollectionMode(startCollecting = true, keepCounts = false) + } + override fun onGetName(node: AbstractComposeView): String = node.javaClass.simpleName private fun transform( From 65adafee2ed5b9bedf173762af720d89d534ec0f Mon Sep 17 00:00:00 2001 From: generatedunixname89002005306973 Date: Wed, 15 May 2024 05:45:36 -0700 Subject: [PATCH 26/92] Flipper Release: v0.253.0 Summary: Releasing version 0.253.0 Reviewed By: antonk52 Differential Revision: D57376489 fbshipit-source-id: cdeeb421a44fe10698aedd7f3041947eb078ef05 --- desktop/package.json | 2 +- desktop/plugins/public/layout/docs/setup.mdx | 2 +- desktop/plugins/public/leak_canary/docs/setup.mdx | 2 +- desktop/plugins/public/network/docs/setup.mdx | 2 +- desktop/static/CHANGELOG.md | 8 ++++++++ docs/getting-started/android-native.mdx | 4 ++-- docs/getting-started/react-native-ios.mdx | 2 +- docs/getting-started/react-native.mdx | 4 ++-- gradle.properties | 2 +- js/js-flipper/package.json | 2 +- react-native/react-native-flipper/package.json | 2 +- 11 files changed, 20 insertions(+), 12 deletions(-) diff --git a/desktop/package.json b/desktop/package.json index 2502ca25fda..482f25dc3f1 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -164,7 +164,7 @@ "npm": "use yarn instead", "yarn": "^1.16" }, - "version": "0.252.0", + "version": "0.253.0", "workspaces": { "packages": [ "scripts", diff --git a/desktop/plugins/public/layout/docs/setup.mdx b/desktop/plugins/public/layout/docs/setup.mdx index 65c4f8b39ad..2b1f81d920f 100644 --- a/desktop/plugins/public/layout/docs/setup.mdx +++ b/desktop/plugins/public/layout/docs/setup.mdx @@ -27,7 +27,7 @@ You also need to compile in the `litho-annotations` package, as Flipper reflects ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.252.0' + debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.253.0' debugImplementation 'com.facebook.litho:litho-annotations:0.19.0' // ... } diff --git a/desktop/plugins/public/leak_canary/docs/setup.mdx b/desktop/plugins/public/leak_canary/docs/setup.mdx index 5b233281786..ab56c22656c 100644 --- a/desktop/plugins/public/leak_canary/docs/setup.mdx +++ b/desktop/plugins/public/leak_canary/docs/setup.mdx @@ -8,7 +8,7 @@ To setup the LeakCan ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.252.0' + debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.253.0' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1' } ``` diff --git a/desktop/plugins/public/network/docs/setup.mdx b/desktop/plugins/public/network/docs/setup.mdx index 8be5d5a9c80..c3c065c3f07 100644 --- a/desktop/plugins/public/network/docs/setup.mdx +++ b/desktop/plugins/public/network/docs/setup.mdx @@ -11,7 +11,7 @@ The network plugin is shipped as a separate Maven artifact, as follows: ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.252.0' + debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.253.0' } ``` diff --git a/desktop/static/CHANGELOG.md b/desktop/static/CHANGELOG.md index f9e1a7c5136..411dafb2c49 100644 --- a/desktop/static/CHANGELOG.md +++ b/desktop/static/CHANGELOG.md @@ -1,3 +1,11 @@ +# 0.253.0 (15/5/2024) + + * [D55967421](https://github.com/facebook/flipper/search?q=D55967421&type=Commits) - BloksDebugger - Async component payload support (android only currently) + * [D56065247](https://github.com/facebook/flipper/search?q=D56065247&type=Commits) - [Internal] + * [D56420921](https://github.com/facebook/flipper/search?q=D56420921&type=Commits) - Bloks debugger, Refresh screen, Set sandbox & workplace feedback button moved to right of power search to increase available vertical space + * [D57157243](https://github.com/facebook/flipper/search?q=D57157243&type=Commits) - Added compose menu with ability to show / hide system nodes + + # 0.252.0 (10/4/2024) * [D55696687](https://github.com/facebook/flipper/search?q=D55696687&type=Commits) - UIDebugger fix visualiser shaking diff --git a/docs/getting-started/android-native.mdx b/docs/getting-started/android-native.mdx index 585a0383c95..093ce2f7a44 100644 --- a/docs/getting-started/android-native.mdx +++ b/docs/getting-started/android-native.mdx @@ -24,10 +24,10 @@ repositories { } dependencies { - debugImplementation 'com.facebook.flipper:flipper:0.252.0' + debugImplementation 'com.facebook.flipper:flipper:0.253.0' debugImplementation 'com.facebook.soloader:soloader:0.10.5' - releaseImplementation 'com.facebook.flipper:flipper-noop:0.252.0' + releaseImplementation 'com.facebook.flipper:flipper-noop:0.253.0' } ``` diff --git a/docs/getting-started/react-native-ios.mdx b/docs/getting-started/react-native-ios.mdx index 1656367978e..14af6750b4b 100644 --- a/docs/getting-started/react-native-ios.mdx +++ b/docs/getting-started/react-native-ios.mdx @@ -51,7 +51,7 @@ Add all of the code below to your `ios/Podfile`: platform :ios, '9.0' def flipper_pods() - flipperkit_version = '0.252.0' # should match the version of your Flipper client app + flipperkit_version = '0.253.0' # should match the version of your Flipper client app pod 'FlipperKit', '~>' + flipperkit_version, :configuration => 'Debug' pod 'FlipperKit/FlipperKitLayoutPlugin', '~>' + flipperkit_version, :configuration => 'Debug' pod 'FlipperKit/SKIOSNetworkPlugin', '~>' + flipperkit_version, :configuration => 'Debug' diff --git a/docs/getting-started/react-native.mdx b/docs/getting-started/react-native.mdx index b0c7b132041..b98b66f1f16 100644 --- a/docs/getting-started/react-native.mdx +++ b/docs/getting-started/react-native.mdx @@ -34,7 +34,7 @@ Latest version of Flipper requires react-native 0.69+! If you use react-native < Android: -1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.252.0`. +1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.253.0`. 2. Run `./gradlew clean` in the `android` directory. iOS: @@ -44,7 +44,7 @@ react-native version => 0.69.0 2. Run `pod install --repo-update` in the `ios` directory. react-native version < 0.69.0 -1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.252.0' })`. +1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.253.0' })`. 2. Run `pod install --repo-update` in the `ios` directory. ## Manual Setup diff --git a/gradle.properties b/gradle.properties index 81e28bb035a..01776788301 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. # POM publishing constants -VERSION_NAME=0.252.1-SNAPSHOT +VERSION_NAME=0.253.0 GROUP=com.facebook.flipper SONATYPE_STAGING_PROFILE=comfacebook POM_URL=https://github.com/facebook/flipper diff --git a/js/js-flipper/package.json b/js/js-flipper/package.json index 30399051602..f315a762f2f 100644 --- a/js/js-flipper/package.json +++ b/js/js-flipper/package.json @@ -1,7 +1,7 @@ { "name": "js-flipper", "title": "JS Flipper Bindings for Web-Socket based clients", - "version": "0.252.0", + "version": "0.253.0", "main": "lib/index.js", "browser": { "os": false diff --git a/react-native/react-native-flipper/package.json b/react-native/react-native-flipper/package.json index 1b81e3ee81d..3ee61c94e4c 100644 --- a/react-native/react-native-flipper/package.json +++ b/react-native/react-native-flipper/package.json @@ -1,7 +1,7 @@ { "name": "react-native-flipper", "title": "React Native Flipper Bindings", - "version": "0.252.0", + "version": "0.253.0", "description": "Flipper bindings for React Native", "main": "index.js", "types": "index.d.ts", From 31af02414052e8dbe73bf9d9d10486d9aea3966c Mon Sep 17 00:00:00 2001 From: generatedunixname89002005306973 Date: Wed, 15 May 2024 05:45:36 -0700 Subject: [PATCH 27/92] Flipper Snapshot Bump: v0.253.1-SNAPSHOT Summary: Releasing snapshot version 0.253.1-SNAPSHOT Reviewed By: antonk52 Differential Revision: D57376490 fbshipit-source-id: c9f35baa2950cf8a799b3646ba85ad1a46b752e2 --- docs/getting-started/android-native.mdx | 4 ++-- gradle.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/android-native.mdx b/docs/getting-started/android-native.mdx index 093ce2f7a44..a553fa26005 100644 --- a/docs/getting-started/android-native.mdx +++ b/docs/getting-started/android-native.mdx @@ -124,10 +124,10 @@ repositories { } dependencies { - debugImplementation 'com.facebook.flipper:flipper:0.252.1-SNAPSHOT' + debugImplementation 'com.facebook.flipper:flipper:0.253.1-SNAPSHOT' debugImplementation 'com.facebook.soloader:soloader:0.10.5' - releaseImplementation 'com.facebook.flipper:flipper-noop:0.252.1-SNAPSHOT' + releaseImplementation 'com.facebook.flipper:flipper-noop:0.253.1-SNAPSHOT' } ``` diff --git a/gradle.properties b/gradle.properties index 01776788301..55d1344811d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. # POM publishing constants -VERSION_NAME=0.253.0 +VERSION_NAME=0.253.1-SNAPSHOT GROUP=com.facebook.flipper SONATYPE_STAGING_PROFILE=comfacebook POM_URL=https://github.com/facebook/flipper From b7cc93b6b0a903a1227714537d862c771a4286cd Mon Sep 17 00:00:00 2001 From: Ben Blackburne Date: Wed, 15 May 2024 06:09:14 -0700 Subject: [PATCH 28/92] flipper | availability checks for catalyst Summary: Needed to allow compilation for catalyst when min version < 14 Differential Revision: D57328418 fbshipit-source-id: 0b3b120c83d9d92440828a0d522cac72a35aa4a4 --- iOS/FlipperKit/FlipperClient.mm | 5 +- iOS/FlipperKit/FlipperKitQRReader.h | 3 + iOS/FlipperKit/FlipperKitQRReader.mm | 8 +- .../FlipperKitQRVerifiedCertificateProvider.h | 1 + ...FlipperKitQRVerifiedCertificateProvider.mm | 137 ++++++++++-------- 5 files changed, 87 insertions(+), 67 deletions(-) diff --git a/iOS/FlipperKit/FlipperClient.mm b/iOS/FlipperKit/FlipperClient.mm index 3ccaef33bbf..3c737a84672 100644 --- a/iOS/FlipperKit/FlipperClient.mm +++ b/iOS/FlipperKit/FlipperClient.mm @@ -127,8 +127,11 @@ - (instancetype)init { } #if !TARGET_OS_OSX - [self setBackupCertificateProvider:[[FlipperKitQRVerifiedCertificateProvider + if (@available(macCatalyst 14.0, *)) { + [self + setBackupCertificateProvider:[[FlipperKitQRVerifiedCertificateProvider alloc] initCPPCertificateProvider]]; + } #endif } return self; diff --git a/iOS/FlipperKit/FlipperKitQRReader.h b/iOS/FlipperKit/FlipperKitQRReader.h index fcf58ecd0c4..0962bfedb8f 100644 --- a/iOS/FlipperKit/FlipperKitQRReader.h +++ b/iOS/FlipperKit/FlipperKitQRReader.h @@ -30,6 +30,7 @@ typedef void (^FBFlipperKitQRResult)( /** QRReader controller that displays the camera and attempts to read QR * metadata objects. */ +API_AVAILABLE(macCatalyst(14.0)) @interface FBFlipperKitQRReaderController : UIViewController - (instancetype _Nonnull)initWith:(FBFlipperKitQRResult _Nonnull)completion; @@ -39,6 +40,7 @@ typedef void (^FBFlipperKitQRResult)( /** QRReader prompt. Shows an alert to the user asking to scan a QR with the * device's camera. */ +API_AVAILABLE(macCatalyst(14.0)) @interface FBFlipperKitQRReaderPrompt : UIAlertController - (void)show; @@ -50,6 +52,7 @@ typedef void (^FBFlipperKitQRResult)( /** QRReader entrypoint. */ +API_AVAILABLE(macCatalyst(14.0)) @interface FlipperKitQRReader : NSObject + (void)read:(FBFlipperKitQRResult _Nonnull)result; diff --git a/iOS/FlipperKit/FlipperKitQRReader.mm b/iOS/FlipperKit/FlipperKitQRReader.mm index 39e0f47e334..9f6ec5f45d4 100644 --- a/iOS/FlipperKit/FlipperKitQRReader.mm +++ b/iOS/FlipperKit/FlipperKitQRReader.mm @@ -84,12 +84,16 @@ @interface FBFlipperKitQRReaderController ()< @property(atomic) BOOL isReading; @property(nonatomic, strong) UIWindow* promptWindow; -@property(nonatomic, strong) AVCaptureSession* captureSession; -@property(nonatomic, strong) AVCaptureVideoPreviewLayer* videoPreviewPlayer; +@property(nonatomic, strong) + AVCaptureSession* captureSession API_AVAILABLE(macCatalyst(14.0)); +@property(nonatomic, strong) + AVCaptureVideoPreviewLayer* videoPreviewPlayer API_AVAILABLE( + macCatalyst(14.0)); @property(nonatomic, strong) AVAudioPlayer* audioPlayer; @end +API_AVAILABLE(macCatalyst(14.0)) @implementation FBFlipperKitQRReaderController + (void)readQRWith:(FBFlipperKitQRResult)completion { diff --git a/iOS/FlipperKit/FlipperKitQRVerifiedCertificateProvider.h b/iOS/FlipperKit/FlipperKitQRVerifiedCertificateProvider.h index 4391aba5af2..fdd46b89d15 100644 --- a/iOS/FlipperKit/FlipperKitQRVerifiedCertificateProvider.h +++ b/iOS/FlipperKit/FlipperKitQRVerifiedCertificateProvider.h @@ -44,6 +44,7 @@ class QRVerifiedCertificateProvider : public FlipperCertificateProvider { } // namespace flipper } // namespace facebook +API_AVAILABLE(macCatalyst(14.0)) @interface FlipperKitQRVerifiedCertificateProvider : NSObject diff --git a/iOS/FlipperKit/FlipperKitQRVerifiedCertificateProvider.mm b/iOS/FlipperKit/FlipperKitQRVerifiedCertificateProvider.mm index 58150bf2170..31d7f7d1d35 100644 --- a/iOS/FlipperKit/FlipperKitQRVerifiedCertificateProvider.mm +++ b/iOS/FlipperKit/FlipperKitQRVerifiedCertificateProvider.mm @@ -79,72 +79,81 @@ : nullptr; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - [FlipperKitQRReader read:^( - NSString* key, - NSError* error, - BOOL cancelled, - FBFlipperKitQRResultAck readResultAck) { - if (error || cancelled) { - // If the QR code was cancelled, we should not proceed on further - // requests. - cancelled_ = cancelled; - certError = - [NSError errorWithDomain:FBFlipperKitQRCertificateProviderErrorDomain - code:-1 - userInfo:@{ - NSLocalizedDescriptionKey : NSLocalizedString( - @"Unable to read key from QR code", nil), - }]; + if (@available(macCatalyst 14.0, *)) { + [FlipperKitQRReader read:^( + NSString* key, + NSError* error, + BOOL cancelled, + FBFlipperKitQRResultAck readResultAck) { + if (error || cancelled) { + // If the QR code was cancelled, we should not proceed on further + // requests. + cancelled_ = cancelled; + certError = [NSError + errorWithDomain:FBFlipperKitQRCertificateProviderErrorDomain + code:-1 + userInfo:@{ + NSLocalizedDescriptionKey : NSLocalizedString( + @"Unable to read key from QR code", nil), + }]; + dispatch_semaphore_signal(semaphore); + return; + } + + // Get the data from the base64-encoded key. + NSData* keyData = [[NSData alloc] initWithBase64EncodedString:key + options:0]; + // QR codes may be corrupt, invalid, or just contain a different + // content to the one that is expected i.e. base-64 encoded key. + // If that is the case, then just return with an error. + if (!keyData) { + readResultAck(QRReaderResultError); + return; + } + + auto success = AESDecrypt( + [encryptedCertificatesPath UTF8String], + [certificatesPath UTF8String], + (const unsigned char*)[keyData bytes]); + + // If the decryption failed, must likely, the key is invalid which may be + // caused by an erroneous QR read. + if (!success) { + readResultAck(QRReaderResultError); + return; + } + + // If the decryption was successful, we can now read the certificates. + // The certificates are stored in a zip file. Unzip to destination. + + [SSZipArchive unzipFileAtPath:certificatesPath toDestination:destination]; + + // Remove the certificate zip file. + [fileManager removeItemAtPath:certificatesPath error:nil]; + + // At this stage, dismiss the QR code reader. + readResultAck(QRReaderResultAccepted); + + if (QRReadStep) { + QRReadStep->complete(); + } + + // Signal the semaphore as to unblock the connection thread. dispatch_semaphore_signal(semaphore); return; - } - - // Get the data from the base64-encoded key. - NSData* keyData = [[NSData alloc] initWithBase64EncodedString:key - options:0]; - // QR codes may be corrupt, invalid, or just contain a different - // content to the one that is expected i.e. base-64 encoded key. - // If that is the case, then just return with an error. - if (!keyData) { - readResultAck(QRReaderResultError); - return; - } - - auto success = AESDecrypt( - [encryptedCertificatesPath UTF8String], - [certificatesPath UTF8String], - (const unsigned char*)[keyData bytes]); - - // If the decryption failed, must likely, the key is invalid which may be - // caused by an erroneous QR read. - if (!success) { - readResultAck(QRReaderResultError); - return; - } - - // If the decryption was successful, we can now read the certificates. - // The certificates are stored in a zip file. Unzip to destination. - - [SSZipArchive unzipFileAtPath:certificatesPath toDestination:destination]; - - // Remove the certificate zip file. - [fileManager removeItemAtPath:certificatesPath error:nil]; - - // At this stage, dismiss the QR code reader. - readResultAck(QRReaderResultAccepted); - - if (QRReadStep) { - QRReadStep->complete(); - } - - // Signal the semaphore as to unblock the connection thread. - dispatch_semaphore_signal(semaphore); - return; - }]; - - // Wait for the semaphore to be signalled. This will block the connection - // thread until the QR code is read or an error takes place. - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + }]; + // Wait for the semaphore to be signalled. This will block the connection + // thread until the QR code is read or an error takes place. + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + } else { + certError = [NSError + errorWithDomain:FBFlipperKitQRCertificateProviderErrorDomain + code:-1 + userInfo:@{ + NSLocalizedDescriptionKey : NSLocalizedString( + @"Unsupported platform for QR Certificate Provider", nil), + }]; + } // Remove the encrypted certificates. [fileManager removeItemAtPath:encryptedCertificatesPath error:nil]; From b18ab3b43e434db621953bfe89ed0ea2951d49af Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Thu, 16 May 2024 06:04:07 -0700 Subject: [PATCH 29/92] Connectivity log event when plugin init / fetch fails Summary: When plugins are added late on the client it tells the desktop to refresh (this call can fail) and the code previously would wipe all plugins. Instead we keep whatever plugins we had and send a connectivity log event Reviewed By: passy Differential Revision: D57388180 fbshipit-source-id: a80ae939866160ecc6f3c73573904623ef08a25c --- desktop/flipper-common/src/server-types.tsx | 5 ++ .../flipper-server/src/FlipperServerImpl.tsx | 9 +++ desktop/flipper-server/src/recorder.tsx | 4 +- desktop/flipper-ui/src/Client.tsx | 65 ++++++++++++------- 4 files changed, 56 insertions(+), 27 deletions(-) diff --git a/desktop/flipper-common/src/server-types.tsx b/desktop/flipper-common/src/server-types.tsx index 20852bea5ef..a57dd091670 100644 --- a/desktop/flipper-common/src/server-types.tsx +++ b/desktop/flipper-common/src/server-types.tsx @@ -292,6 +292,11 @@ export type FlipperServerCommands = { serial: string, destination: string, ) => Promise; + 'log-connectivity-event': ( + level: 'info' | 'warning' | 'error', + query: ClientQuery | null, + ...message: any + ) => Promise; 'device-stop-screencapture': (serial: string) => Promise; // file path 'device-shell-exec': (serial: string, command: string) => Promise; 'device-install-app': ( diff --git a/desktop/flipper-server/src/FlipperServerImpl.tsx b/desktop/flipper-server/src/FlipperServerImpl.tsx index 98f7374d3cc..7f16603702f 100644 --- a/desktop/flipper-server/src/FlipperServerImpl.tsx +++ b/desktop/flipper-server/src/FlipperServerImpl.tsx @@ -30,6 +30,7 @@ import { DeviceDebugData, CertificateExchangeMedium, Settings, + ClientQuery, } from 'flipper-common'; import {ServerDevice} from './devices/ServerDevice'; import {Base64} from 'js-base64'; @@ -64,6 +65,7 @@ import {movePWA} from './utils/findInstallation'; import GK from './fb-stubs/GK'; import {fetchNewVersion} from './fb-stubs/fetchNewVersion'; import dns from 'dns'; +import {recorder} from './recorder'; // The default on node16 is to prefer ipv4 results which causes issues // in some setups. @@ -471,6 +473,13 @@ export class FlipperServerImpl implements FlipperServer { isSymbolicLink: stats.isSymbolicLink(), }; }, + 'log-connectivity-event': async ( + level: 'info' | 'warning' | 'error', + query: ClientQuery | null, + message: any[], + ) => { + recorder.log_(level, query ?? recorder.undefinedClientQuery_, message); + }, 'node-api-fs-readlink': readlink, 'node-api-fs-readfile': async (path, options) => { const contents = await readFile(path, options ?? 'utf8'); diff --git a/desktop/flipper-server/src/recorder.tsx b/desktop/flipper-server/src/recorder.tsx index 2abc1494923..63237f92215 100644 --- a/desktop/flipper-server/src/recorder.tsx +++ b/desktop/flipper-server/src/recorder.tsx @@ -31,7 +31,7 @@ type ConnectionRecorderEvents = { class Recorder { private flipperServer_: FlipperServerImpl | undefined; - private undefinedClientQuery_: ClientQuery = { + undefinedClientQuery_: ClientQuery = { app: 'NONE', app_id: 'NONE', device: 'NONE', @@ -72,7 +72,7 @@ class Recorder { }, }; - private log_ = ( + log_ = ( type: 'info' | 'warning' | 'error', clientQuery: ClientQuery, ...args: any[] diff --git a/desktop/flipper-ui/src/Client.tsx b/desktop/flipper-ui/src/Client.tsx index 3145513da2c..349138c21b8 100644 --- a/desktop/flipper-ui/src/Client.tsx +++ b/desktop/flipper-ui/src/Client.tsx @@ -192,20 +192,30 @@ export default class Client extends EventEmitter { } async init() { - await this.loadPlugins('init'); - await Promise.all( - [...this.plugins].map(async (pluginId) => - this.startPluginIfNeeded(await this.getPlugin(pluginId)), - ), - ); - this.backgroundPlugins = new Set(await this.getBackgroundPlugins()); - this.backgroundPlugins.forEach((plugin) => { - if (this.shouldConnectAsBackgroundPlugin(plugin)) { - this.initPlugin(plugin); - } - }); - this.emit('plugins-change'); - this.resolveInitPromise?.(null); + try { + await this.loadPlugins('init'); + await Promise.all( + [...this.plugins].map(async (pluginId) => + this.startPluginIfNeeded(await this.getPlugin(pluginId)), + ), + ); + this.backgroundPlugins = new Set(await this.getBackgroundPlugins()); + this.backgroundPlugins.forEach((plugin) => { + if (this.shouldConnectAsBackgroundPlugin(plugin)) { + this.initPlugin(plugin); + } + }); + this.emit('plugins-change'); + this.resolveInitPromise?.(null); + } catch (e) { + this.flipperServer.exec( + 'log-connectivity-event', + 'error', + this.query, + `Failed to initialise client`, + e, + ); + } } async initFromImport( @@ -225,23 +235,28 @@ export default class Client extends EventEmitter { // get the supported plugins async loadPlugins(phase: 'init' | 'refresh'): Promise> { - let response; try { - response = await timeout( + const response = await timeout( 30 * 1000, this.rawCall<{plugins: Plugins}>('getPlugins', false), 'Fetch plugin timeout. Unresponsive client?', ); + this.plugins = new Set(response.plugins ?? []); + console.info( + `Received plugins from '${this.query.app}' on device '${this.query.device}'`, + [...this.plugins], + ); + if (phase === 'init') { + await this.waitForPluginsInit(); + } } catch (e) { - console.warn('Failed to fetch plugin', e); - } - this.plugins = new Set(response?.plugins ?? []); - console.info( - `Received plugins from '${this.query.app}' on device '${this.query.device}'`, - [...this.plugins], - ); - if (phase === 'init') { - await this.waitForPluginsInit(); + this.flipperServer.exec( + 'log-connectivity-event', + 'error', + this.query, + `Failed to fetch plugins for phase ${phase}`, + e, + ); } return this.plugins; } From c432ad45b2d25c9f069596431481a993da0629d7 Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Thu, 16 May 2024 06:04:07 -0700 Subject: [PATCH 30/92] show notification when plugin fetch fails Summary: See earlier diff, Adding notification for when plugin fetch fails Reviewed By: lblasa Differential Revision: D57388178 fbshipit-source-id: 2f80d129f470fbe7adb73fab9e03bef230bede73 --- desktop/flipper-ui/src/Client.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/desktop/flipper-ui/src/Client.tsx b/desktop/flipper-ui/src/Client.tsx index 349138c21b8..366d1376333 100644 --- a/desktop/flipper-ui/src/Client.tsx +++ b/desktop/flipper-ui/src/Client.tsx @@ -33,7 +33,7 @@ import { createState, getFlipperLib, } from 'flipper-plugin'; -import {message} from 'antd'; +import {message, notification} from 'antd'; import { isFlipperMessageDebuggingEnabled, registerFlipperDebugMessage, @@ -257,6 +257,12 @@ export default class Client extends EventEmitter { `Failed to fetch plugins for phase ${phase}`, e, ); + + notification.error({ + key: `plugin-init-error${this.id}`, + duration: 10, + message: `Failed to initialise client ${this.query.app}, please restart the app`, + }); } return this.plugins; } From f049151d1cf1c865a180dd94723e40d22126c4bd Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Thu, 16 May 2024 06:04:07 -0700 Subject: [PATCH 31/92] add retry and shorter individual timeout when fetching plugins Summary: The old approach did a single request with along 30 second timeout., instead we retry multiple times but preserve the same overall timeout Reviewed By: lblasa Differential Revision: D57388179 fbshipit-source-id: 1875e73eed322538f74f4b7366e01e1f2bed1620 --- desktop/flipper-ui/package.json | 3 ++- desktop/flipper-ui/src/Client.tsx | 25 +++++++++++++++++++++---- desktop/yarn.lock | 5 +++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/desktop/flipper-ui/package.json b/desktop/flipper-ui/package.json index 32961c9d698..448d079df81 100644 --- a/desktop/flipper-ui/package.json +++ b/desktop/flipper-ui/package.json @@ -72,7 +72,8 @@ "metro-runtime": "^0.70.2", "pretty-format": "^29.7.0", "react-refresh": "^0.14.0", - "redux-mock-store": "^1.0.1" + "redux-mock-store": "^1.0.1", + "ts-retry-promise": "^0.8.0" }, "peerDependencies": {}, "scripts": { diff --git a/desktop/flipper-ui/src/Client.tsx b/desktop/flipper-ui/src/Client.tsx index 366d1376333..d895829ca93 100644 --- a/desktop/flipper-ui/src/Client.tsx +++ b/desktop/flipper-ui/src/Client.tsx @@ -45,6 +45,7 @@ import {EventEmitter} from 'eventemitter3'; import {createServerAddOnControls} from './utils/createServerAddOnControls'; import isProduction from './utils/isProduction'; import {freeze} from 'immer'; +import {retry} from 'ts-retry-promise'; type Plugins = Set; type PluginsArr = Array; @@ -236,10 +237,26 @@ export default class Client extends EventEmitter { // get the supported plugins async loadPlugins(phase: 'init' | 'refresh'): Promise> { try { - const response = await timeout( - 30 * 1000, - this.rawCall<{plugins: Plugins}>('getPlugins', false), - 'Fetch plugin timeout. Unresponsive client?', + const response = await retry( + () => + //shortish timeout for each individual request + timeout( + 5 * 1000, + this.rawCall<{plugins: Plugins}>('getPlugins', false), + 'Fetch plugin timeout. Unresponsive client?', + ), + { + retries: 5, + delay: 1000, + backoff: 'LINEAR', + logger: (msg) => + this.flipperServer.exec( + 'log-connectivity-event', + 'warning', + this.query, + `Attempt to fetch plugins failed for phase ${phase}, Error: ${msg}`, + ), + }, ); this.plugins = new Set(response.plugins ?? []); console.info( diff --git a/desktop/yarn.lock b/desktop/yarn.lock index e66c982b150..62f618e0a9a 100644 --- a/desktop/yarn.lock +++ b/desktop/yarn.lock @@ -12401,6 +12401,11 @@ ts-node@^10.9.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" +ts-retry-promise@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/ts-retry-promise/-/ts-retry-promise-0.8.0.tgz#0a0c57b510827d5630da4b0c47f36b55b83a49e3" + integrity sha512-elI/GkojPANBikPaMWQnk4T/bOJ6tq/hqXyQRmhfC9PAD6MoHmXIXK7KilJrlpx47VAKCGcmBrTeK5dHk6YAYg== + tsconfig-paths@^3.15.0: version "3.15.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" From b28239229f165920b8edcc8f58db0065c78b4e68 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Tue, 21 May 2024 04:59:55 -0700 Subject: [PATCH 32/92] handle empty output from idb list-targets Summary: `idb list-targets --json` outputs empty string, which is not a valid JSON. Reviewed By: nikoant Differential Revision: D57614884 fbshipit-source-id: 572e9e5e44be1cde04d0c74fbff44a92d2a72fd3 --- desktop/doctor/src/index.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/desktop/doctor/src/index.tsx b/desktop/doctor/src/index.tsx index ea932b89bd6..13150861ba3 100644 --- a/desktop/doctor/src/index.tsx +++ b/desktop/doctor/src/index.tsx @@ -279,8 +279,14 @@ export function getHealthchecks(): FlipperDoctor.Healthchecks { const devices = result.stdout .trim() .split('\n') - .map((x) => JSON.parse(x)) - .filter((x) => x.type === 'simulator'); + .map((x) => { + try { + return JSON.parse(x); + } catch (e) { + return null; + } + }) + .filter((x) => x != null && x.type === 'simulator'); if (devices.length === 0) { return { From 9f345043f347d8c9c00ca5f5ce4f3a8c9cd14544 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Thu, 23 May 2024 08:02:44 -0700 Subject: [PATCH 33/92] make idb required and first ios doctor check Summary: While technically idb is optional. Requiring it to be installed makes it easier to reason about when helping people with connectivity issues. Reviewed By: passy Differential Revision: D57723103 fbshipit-source-id: 685c2fa5475272a99effed5231dd3688555aa1e8 --- desktop/doctor/src/index.tsx | 118 +++++++++++++++++------------------ 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/desktop/doctor/src/index.tsx b/desktop/doctor/src/index.tsx index 13150861ba3..efc956a14df 100644 --- a/desktop/doctor/src/index.tsx +++ b/desktop/doctor/src/index.tsx @@ -159,6 +159,65 @@ export function getHealthchecks(): FlipperDoctor.Healthchecks { isRequired: false, isSkipped: false, healthchecks: [ + { + key: 'ios.idb', + label: 'IDB installed', + isRequired: true, + run: async ( + _: FlipperDoctor.EnvironmentInfo, + settings?: {enablePhysicalIOS: boolean; idbPath: string}, + ): Promise => { + if (!settings) { + return { + hasProblem: false, + message: ['ios.idb--no_context'], + }; + } + if (!settings.enablePhysicalIOS) { + return { + hasProblem: false, + message: ['ios.idb--physical_device_disabled'], + }; + } + const result = await tryExecuteCommand( + `${settings?.idbPath} --help`, + ); + const hasIdbCompanion = + await tryExecuteCommand(`idbCompanion --help`); + if (result.fail) { + const hasIdbInPath = await tryExecuteCommand(`which idb`); + + if (!hasIdbInPath.fail) { + return { + hasProblem: true, + message: [ + 'ios.idb--not_installed_but_present', + { + idbPath: settings.idbPath, + idbInPath: hasIdbInPath.stdout.trim(), + }, + ], + }; + } + + return { + hasProblem: true, + message: [ + 'ios.idb--not_installed', + { + idbPath: settings.idbPath, + hasIdbCompanion: !hasIdbCompanion.fail, + }, + ], + }; + } + + return { + hasProblem: false, + message: ['ios.idb--installed'], + }; + }, + }, { key: 'ios.xcode', label: 'XCode Installed', @@ -332,65 +391,6 @@ export function getHealthchecks(): FlipperDoctor.Healthchecks { }; }, }, - { - key: 'ios.idb', - label: 'IDB installed', - isRequired: false, - run: async ( - _: FlipperDoctor.EnvironmentInfo, - settings?: {enablePhysicalIOS: boolean; idbPath: string}, - ): Promise => { - if (!settings) { - return { - hasProblem: false, - message: ['ios.idb--no_context'], - }; - } - if (!settings.enablePhysicalIOS) { - return { - hasProblem: false, - message: ['ios.idb--physical_device_disabled'], - }; - } - const result = await tryExecuteCommand( - `${settings?.idbPath} --help`, - ); - const hasIdbCompanion = - await tryExecuteCommand(`idbCompanion --help`); - if (result.fail) { - const hasIdbInPath = await tryExecuteCommand(`which idb`); - - if (!hasIdbInPath.fail) { - return { - hasProblem: true, - message: [ - 'ios.idb--not_installed_but_present', - { - idbPath: settings.idbPath, - idbInPath: hasIdbInPath.stdout.trim(), - }, - ], - }; - } - - return { - hasProblem: true, - message: [ - 'ios.idb--not_installed', - { - idbPath: settings.idbPath, - hasIdbCompanion: !hasIdbCompanion.fail, - }, - ], - }; - } - - return { - hasProblem: false, - message: ['ios.idb--installed'], - }; - }, - }, ], } : { From 93f378cf39680a9d6b21a9311ef9a94f20dfe067 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Thu, 23 May 2024 08:02:44 -0700 Subject: [PATCH 34/92] provide full command to run for xcode-select Reviewed By: passy Differential Revision: D57724432 fbshipit-source-id: 94aeeed2005c2318ffcf5850c85e08f123c70879 --- desktop/flipper-common/src/doctor.tsx | 7 ++++++- .../flipper-ui/src/sandy-chrome/doctor/index.tsx | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/desktop/flipper-common/src/doctor.tsx b/desktop/flipper-common/src/doctor.tsx index ce7fce328b4..e4ac0e05a64 100644 --- a/desktop/flipper-common/src/doctor.tsx +++ b/desktop/flipper-common/src/doctor.tsx @@ -170,7 +170,12 @@ export namespace FlipperDoctor { 'ios.xcode-select--not_set': [{message: string}]; 'ios.xcode-select--no_xcode_selected': []; 'ios.xcode-select--noop': []; - 'ios.xcode-select--custom_path': []; + 'ios.xcode-select--custom_path': [ + { + selectedPath: string; + availableXcode: string | null; + }, + ]; 'ios.xcode-select--old_version_selected': [ { selectedVersion: string; diff --git a/desktop/flipper-ui/src/sandy-chrome/doctor/index.tsx b/desktop/flipper-ui/src/sandy-chrome/doctor/index.tsx index db881099e2f..898794b679a 100644 --- a/desktop/flipper-ui/src/sandy-chrome/doctor/index.tsx +++ b/desktop/flipper-ui/src/sandy-chrome/doctor/index.tsx @@ -187,6 +187,19 @@ const XcodeSelectNoXcode = ( ); +const XcodeSelectCustomPath = ( + props: PropsFor<'ios.xcode-select--custom_path'>, +) => ( + + Selected path is not a Xcode application: + {props.selectedPath} + /Xcode.app'}`} + /> + +); + const XcodeSelectNonExistingSelected = ( props: PropsFor<'ios.xcode-select--nonexisting_selected'>, ) => ( @@ -335,7 +348,7 @@ const messageToComp: { 'ios.xcode-select--no_xcode_selected': XcodeSelectNoXcode, 'ios.xcode-select--nonexisting_selected': XcodeSelectNonExistingSelected, 'ios.xcode-select--noop': Noop, - 'ios.xcode-select--custom_path': Noop, + 'ios.xcode-select--custom_path': XcodeSelectCustomPath, 'ios.xcode-select--old_version_selected': Noop, 'ios.sdk--installed': IosSdkInstalled, From 3693b4971c62ec08f5766dd0745795f2827343eb Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Thu, 23 May 2024 08:02:44 -0700 Subject: [PATCH 35/92] exact command to run for all xcode select issues when possible Reviewed By: lblasa Differential Revision: D57724756 fbshipit-source-id: fbf0c523def83cb51464720019ea10a3e6cf5210 --- .../fb-stubs/validateSelectedXcodeVersion.tsx | 1 + desktop/doctor/src/index.tsx | 30 ++++++++++++++--- desktop/flipper-common/src/doctor.tsx | 10 ++++-- .../src/sandy-chrome/doctor/index.tsx | 32 +++++++++---------- 4 files changed, 48 insertions(+), 25 deletions(-) diff --git a/desktop/doctor/src/fb-stubs/validateSelectedXcodeVersion.tsx b/desktop/doctor/src/fb-stubs/validateSelectedXcodeVersion.tsx index 185020e92bb..1b5ca7d1e4f 100644 --- a/desktop/doctor/src/fb-stubs/validateSelectedXcodeVersion.tsx +++ b/desktop/doctor/src/fb-stubs/validateSelectedXcodeVersion.tsx @@ -11,6 +11,7 @@ import {FlipperDoctor} from 'flipper-common'; export async function validateSelectedXcodeVersion( _selectedPath: string, + _availableXcode: string | null, ): Promise { return { hasProblem: false, diff --git a/desktop/doctor/src/index.tsx b/desktop/doctor/src/index.tsx index efc956a14df..207153ada9b 100644 --- a/desktop/doctor/src/index.tsx +++ b/desktop/doctor/src/index.tsx @@ -251,22 +251,39 @@ export function getHealthchecks(): FlipperDoctor.Healthchecks { run: async ( _: FlipperDoctor.EnvironmentInfo, ): Promise => { - // TODO check for an existing Xcode + const allApps = + await fs_extra.promises.readdir('/Applications'); + // Xcode_14.2.0_xxxxxxx.app + // Xcode_14.3.1_xxxxxxxxxx.app + // Xcode_15.0.0_xxxxxxxxxx.app + // Xcode.app + const latestXCode = allApps + .filter((a) => a.startsWith('Xcode')) + .sort() + .pop(); + const availableXcode = latestXCode + ? path.join('/Applications', latestXCode) + : null; + const result = await tryExecuteCommand('xcode-select -p'); if (result.fail) { return { hasProblem: true, message: [ 'ios.xcode-select--not_set', - {message: result.message}, + {message: result.message, availableXcode}, ], }; } + const selectedXcode = result.stdout.toString().trim(); if (selectedXcode == '/Library/Developer/CommandLineTools') { return { hasProblem: true, - message: ['ios.xcode-select--no_xcode_selected'], + message: [ + 'ios.xcode-select--no_xcode_selected', + {availableXcode}, + ], }; } if ((await fs_extra.pathExists(selectedXcode)) == false) { @@ -274,12 +291,15 @@ export function getHealthchecks(): FlipperDoctor.Healthchecks { hasProblem: true, message: [ 'ios.xcode-select--nonexisting_selected', - {selected: selectedXcode}, + {selected: selectedXcode, availableXcode}, ], }; } const validatedXcodeVersion = - await validateSelectedXcodeVersion(selectedXcode); + await validateSelectedXcodeVersion( + selectedXcode, + availableXcode, + ); if (validatedXcodeVersion.hasProblem) { return validatedXcodeVersion; } diff --git a/desktop/flipper-common/src/doctor.tsx b/desktop/flipper-common/src/doctor.tsx index e4ac0e05a64..7798b635669 100644 --- a/desktop/flipper-common/src/doctor.tsx +++ b/desktop/flipper-common/src/doctor.tsx @@ -167,8 +167,10 @@ export namespace FlipperDoctor { 'ios.xcode--not_installed': []; 'ios.xcode-select--set': [{selected: string}]; - 'ios.xcode-select--not_set': [{message: string}]; - 'ios.xcode-select--no_xcode_selected': []; + 'ios.xcode-select--not_set': [ + {message: string; availableXcode: string | null}, + ]; + 'ios.xcode-select--no_xcode_selected': [{availableXcode: string | null}]; 'ios.xcode-select--noop': []; 'ios.xcode-select--custom_path': [ { @@ -182,7 +184,9 @@ export namespace FlipperDoctor { latestXCode: string; }, ]; - 'ios.xcode-select--nonexisting_selected': [{selected: string}]; + 'ios.xcode-select--nonexisting_selected': [ + {selected: string; availableXcode: string | null}, + ]; 'ios.sdk--installed': [{platforms: string[]}]; 'ios.sdk--not_installed': []; diff --git a/desktop/flipper-ui/src/sandy-chrome/doctor/index.tsx b/desktop/flipper-ui/src/sandy-chrome/doctor/index.tsx index 898794b679a..8f7dd4e2a53 100644 --- a/desktop/flipper-ui/src/sandy-chrome/doctor/index.tsx +++ b/desktop/flipper-ui/src/sandy-chrome/doctor/index.tsx @@ -33,6 +33,16 @@ const CommonOpenSSLInstalled = ( {props.output} ); +const XcodeSelectSwitch = ({ + availableXcode, +}: { + availableXcode: string | null; +}) => ( + /Xcode.app'}`} + /> +); const CommonOpenSSLNotInstalled = ( props: PropsFor<'common.openssl--not_installed'>, @@ -162,28 +172,20 @@ const XcodeSelectSet = (props: PropsFor<'ios.xcode-select--set'>) => ( {props.selected} ); -const XcodeSelectNotSet = (_props: PropsFor<'ios.xcode-select--not_set'>) => ( +const XcodeSelectNotSet = (props: PropsFor<'ios.xcode-select--not_set'>) => ( xcode-select path not selected. xcode-select -p failed. To fix it run this command: - /Xcode.app`} - /> + ); const XcodeSelectNoXcode = ( - _props: PropsFor<'ios.xcode-select--no_xcode_selected'>, + props: PropsFor<'ios.xcode-select--no_xcode_selected'>, ) => ( xcode-select has no Xcode selected. To fix it it run this command: - /Xcode.app`} - /> + ); @@ -206,11 +208,7 @@ const XcodeSelectNonExistingSelected = ( xcode-select is pointing at a path that does not exist: {props.selected} - /Xcode.app`} - /> + ); From 4f8532227faeaa8e640582df29b68510369e4e54 Mon Sep 17 00:00:00 2001 From: Pascal Hartig Date: Tue, 28 May 2024 03:15:42 -0700 Subject: [PATCH 36/92] Fix typo Summary: $title Reviewed By: antonk52 Differential Revision: D57783441 fbshipit-source-id: 93d8069493560efd70d08402b29592ea3ea60fd6 --- desktop/plugins/public/logs/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/plugins/public/logs/index.tsx b/desktop/plugins/public/logs/index.tsx index a1635caed3e..8192bcfbf3a 100644 --- a/desktop/plugins/public/logs/index.tsx +++ b/desktop/plugins/public/logs/index.tsx @@ -159,7 +159,7 @@ export function devicePlugin(client: DevicePluginClient) { client.onDeepLink((payload: unknown) => { if (typeof payload === 'string') { tableManagerRef.current?.setSearchExpression(powerSearchInitialState); - // timeout as we want to await restoring any previous scroll positin first, then scroll to the + // timeout as we want to await restoring any previous scroll position first, then scroll to them setTimeout(() => { let hasMatch = false; rows.view.output(0, rows.view.size).forEach((row, index) => { From 8b946d4cc58cfed5da1db97d66f121e19ef1ef5b Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Tue, 28 May 2024 10:07:56 -0700 Subject: [PATCH 37/92] support big int in data inspector Reviewed By: antonk52 Differential Revision: D57780901 fbshipit-source-id: 5d66b0dcc47531d3cf0eae91f167f533c9cd7734 --- .../src/ui/data-inspector/DataInspectorNode.tsx | 4 ++++ desktop/flipper-plugin/src/ui/data-inspector/DataPreview.tsx | 3 +++ 2 files changed, 7 insertions(+) diff --git a/desktop/flipper-plugin/src/ui/data-inspector/DataInspectorNode.tsx b/desktop/flipper-plugin/src/ui/data-inspector/DataInspectorNode.tsx index c0154d59ceb..3a099aad9a1 100644 --- a/desktop/flipper-plugin/src/ui/data-inspector/DataInspectorNode.tsx +++ b/desktop/flipper-plugin/src/ui/data-inspector/DataInspectorNode.tsx @@ -193,6 +193,10 @@ const defaultValueExtractor: DataValueExtractor = (value: any) => { return {mutable: true, type: 'number', value}; } + if (type === 'bigint') { + return {mutable: true, type: 'bigint', value}; + } + if (type === 'string') { return {mutable: true, type: 'string', value}; } diff --git a/desktop/flipper-plugin/src/ui/data-inspector/DataPreview.tsx b/desktop/flipper-plugin/src/ui/data-inspector/DataPreview.tsx index 12b8bc68aed..dbe339c8057 100755 --- a/desktop/flipper-plugin/src/ui/data-inspector/DataPreview.tsx +++ b/desktop/flipper-plugin/src/ui/data-inspector/DataPreview.tsx @@ -86,6 +86,9 @@ export default class DataPreview extends PureComponent<{ case 'number': propValueElement = {`${propValue}`}; break; + case 'bigint': + propValueElement = {`${propValue}`}; + break; case 'string': if (propValue.length <= 20) { propValueElement = {propValue}; From 1e683cbba82f1e7379d5b565efc832fa5593b056 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Tue, 28 May 2024 12:13:58 -0700 Subject: [PATCH 38/92] update ts-retry-promise Summary: No breaking changes to how we use the library prime reason to update - added docs to config fields [release notes](https://github.com/normartin/ts-retry-promise/releases) Reviewed By: passy Differential Revision: D57857760 fbshipit-source-id: ada8b7a7f4874eb0435834247184fb52308812c1 --- desktop/plugins/public/ui-debugger/package.json | 2 +- desktop/plugins/public/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/desktop/plugins/public/ui-debugger/package.json b/desktop/plugins/public/ui-debugger/package.json index 8326f56f844..03128a41c04 100644 --- a/desktop/plugins/public/ui-debugger/package.json +++ b/desktop/plugins/public/ui-debugger/package.json @@ -19,7 +19,7 @@ "react-query": "^3.39.1", "async": "2.3.0", "@tanstack/react-virtual": "3.0.0-beta.54", - "ts-retry-promise": "^0.7.0", + "ts-retry-promise": "^0.8.1", "memoize-weak": "^1.0.2", "eventemitter3": "^4.0.7" }, diff --git a/desktop/plugins/public/yarn.lock b/desktop/plugins/public/yarn.lock index 98be50c85f7..df49edb89f7 100644 --- a/desktop/plugins/public/yarn.lock +++ b/desktop/plugins/public/yarn.lock @@ -2351,10 +2351,10 @@ ts-node@^10.9.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" -ts-retry-promise@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/ts-retry-promise/-/ts-retry-promise-0.7.0.tgz#08f2dcbbf5d2981495841cb63389a268324e8147" - integrity sha512-x6yWZXC4BfXy4UyMweOFvbS1yJ/Y5biSz/mEPiILtJZLrqD3ZxIpzVOGGgifHHdaSe3WxzFRtsRbychI6zofOg== +ts-retry-promise@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/ts-retry-promise/-/ts-retry-promise-0.8.1.tgz#ba90eb07cb03677fcbf78fe38e94c9183927e154" + integrity sha512-+AHPUmAhr5bSRRK5CurE9kNH8gZlEHnCgusZ0zy2bjfatUBDX0h6vGQjiT0YrGwSDwRZmU+bapeX6mj55FOPvg== tslib@^2.1.0: version "2.6.2" From d00bf361121f6a607e37d00444d74ef6edff443c Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Tue, 28 May 2024 16:24:48 -0700 Subject: [PATCH 39/92] install ios sdk from settings Reviewed By: LukeDefeo Differential Revision: D57866918 fbshipit-source-id: 601833dc4151ce4f2fcc0a626bc569bafa40bf2e --- .../troubleshooting/install-ios-sdk.mdx | 15 ++++++++++++++- website/static/img/ios-sdk-2-1.png | Bin 0 -> 170098 bytes website/static/img/ios-sdk-2-2.png | Bin 0 -> 390408 bytes 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 website/static/img/ios-sdk-2-1.png create mode 100644 website/static/img/ios-sdk-2-2.png diff --git a/docs/getting-started/troubleshooting/install-ios-sdk.mdx b/docs/getting-started/troubleshooting/install-ios-sdk.mdx index 49ef5064735..540e49a00b1 100644 --- a/docs/getting-started/troubleshooting/install-ios-sdk.mdx +++ b/docs/getting-started/troubleshooting/install-ios-sdk.mdx @@ -1,5 +1,5 @@ import useBaseUrl from '@docusaurus/useBaseUrl'; -export const imgStyle = {display: 'block', margin: 'auto', maxWidth: 600} +export const imgStyle = {display: 'block', margin: '0 auto 16px', maxWidth: 600} # Install iOS SDK @@ -15,6 +15,19 @@ Make sure Xcode is installed. Launch Xcode. +
+Don't see the "sdk install prompt"? + +- Select "Xcode" > "Settings" + + + +- Make sure that iOS is installed. Click "Get" if it is not installed. + + + +
+ - Wait for SDK's to install diff --git a/website/static/img/ios-sdk-2-1.png b/website/static/img/ios-sdk-2-1.png new file mode 100644 index 0000000000000000000000000000000000000000..7e7653883b38036c2dfc619cc6301df348cacc12 GIT binary patch literal 170098 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 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�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 vvm!e2(`M|`B8E|N7plAaJ@@Am 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⪻$_r!TX`cF1ss#F*)) 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μ=dloI68QlpN^! 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�Oy5Z@V&dxalZFvRr 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ℜwV`#S1cBy+g(-}@ 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 literal 0 HcmV?d00001 From c4338697dd98e8d9e157fe1f840707c364053b96 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Tue, 28 May 2024 16:24:48 -0700 Subject: [PATCH 40/92] update error message for no ios devices available Summary: People typically can see face this error when an old Xcode version was removed where `xcode-select` was pointing and they do not have SDK installed for a new Xcode versoin. Happy to improve this message further. Differential Revision: D57867332 fbshipit-source-id: da3f260c5ab19d45f1e86d8356d1d25b877f9489 --- desktop/flipper-ui/src/sandy-chrome/doctor/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desktop/flipper-ui/src/sandy-chrome/doctor/index.tsx b/desktop/flipper-ui/src/sandy-chrome/doctor/index.tsx index 8f7dd4e2a53..6a362c01f0e 100644 --- a/desktop/flipper-ui/src/sandy-chrome/doctor/index.tsx +++ b/desktop/flipper-ui/src/sandy-chrome/doctor/index.tsx @@ -254,8 +254,8 @@ const HasSimulatorsNoDevices = ( _props: PropsFor<'ios.has-simulators--no-devices'>, ) => ( - No available simulators found. Launch XCode and install SDK for iOS or run{' '} - xcode-select --install + No available simulators found. This can happen because{' '} + xcode-select points to a wrong Xcode installation. ); From 4c79bc8a25afd659a4bbf83fee7819cc106a2e31 Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Wed, 29 May 2024 03:08:19 -0700 Subject: [PATCH 41/92] Tweak wire format for additional data collection and dont serialize null Summary: before we were adding additionalCollecitonStatus to every node with Unavailable. This is kind of wasteful so instead lets use a nullable boolean. The json serializer has been updated accordingly Reviewed By: zielinskimz Differential Revision: D57868606 fbshipit-source-id: 2d10673dab21506b2107486f549e2f9897383963 --- .../uidebugger/core/LayoutTraversal.kt | 20 ++++++++----------- .../plugins/uidebugger/core/UpdateQueue.kt | 7 ++++--- .../flipper/plugins/uidebugger/model/Node.kt | 9 +-------- 3 files changed, 13 insertions(+), 23 deletions(-) diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt index 3b94fc7f77e..1361ca17190 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt @@ -11,7 +11,6 @@ import android.util.Log import com.facebook.flipper.plugins.uidebugger.LogTag import com.facebook.flipper.plugins.uidebugger.descriptors.Id import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor -import com.facebook.flipper.plugins.uidebugger.model.AdditionalDataStatus import com.facebook.flipper.plugins.uidebugger.model.Node import com.facebook.flipper.plugins.uidebugger.model.TraversalError import com.facebook.flipper.plugins.uidebugger.util.Immediate @@ -66,7 +65,7 @@ class LayoutTraversal( emptySet(), emptyList(), null, - AdditionalDataStatus.NOT_AVAILABLE))) + null))) shallow.remove(node) continue @@ -100,16 +99,13 @@ class LayoutTraversal( val tags = descriptor.getTags(node) visited.add( attributesInfo.map { attrsInfo -> - val additionalDataStatus = - when (!shouldGetAdditionalData && !attrsInfo.hasAdditionalData) { - true -> AdditionalDataStatus.NOT_AVAILABLE - false -> { - when (shouldGetAdditionalData) { - true -> AdditionalDataStatus.ENABLED - false -> AdditionalDataStatus.DISABLED - } - } + val additionalDataCollection = + if (!shouldGetAdditionalData && !attrsInfo.hasAdditionalData) { + null + } else { + shouldGetAdditionalData } + Node( curId, parentId, @@ -123,7 +119,7 @@ class LayoutTraversal( tags, childrenIds, activeChildId, - additionalDataStatus) + additionalDataCollection) }) } catch (exception: Exception) { Log.e(LogTag, "Error while processing node ${node.javaClass.name} $node", exception) diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/UpdateQueue.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/UpdateQueue.kt index fd874f37ec0..63f5752d5b4 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/UpdateQueue.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/UpdateQueue.kt @@ -45,6 +45,7 @@ data class Update( */ class UpdateQueue(val context: UIDContext) { + val json = Json { explicitNulls = false } // conflated channel means we only hold 1 item and newer values override older ones, // there is no point processing frames that the desktop cant keep up with since we only display // the latest @@ -123,7 +124,7 @@ class UpdateQueue(val context: UIDContext) { val (serialized, serializationTimeMs) = StopWatch.time { - Json.encodeToString( + json.encodeToString( FrameScanEvent.serializer(), FrameScanEvent(update.startTimestamp, nodes, snapshot, frameworkEvents)) } @@ -151,7 +152,7 @@ class UpdateQueue(val context: UIDContext) { frameworkEventsCount = frameworkEvents.size) context.connectionRef.connection?.send( - PerfStatsEvent.name, Json.encodeToString(PerfStatsEvent.serializer(), perfStats)) + PerfStatsEvent.name, json.encodeToString(PerfStatsEvent.serializer(), perfStats)) } private fun sendMetadata() { @@ -159,7 +160,7 @@ class UpdateQueue(val context: UIDContext) { if (metadata.isNotEmpty()) { context.connectionRef.connection?.send( MetadataUpdateEvent.name, - Json.encodeToString(MetadataUpdateEvent.serializer(), MetadataUpdateEvent(metadata))) + json.encodeToString(MetadataUpdateEvent.serializer(), MetadataUpdateEvent(metadata))) } } } diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/Node.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/Node.kt index ecad357f01d..52941b9030b 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/Node.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/model/Node.kt @@ -25,7 +25,7 @@ data class Node( val tags: Set, val children: List, val activeChild: Id?, - val additionalDataStatus: AdditionalDataStatus, + val additionalDataCollection: Boolean?, ) /** Expected order is left right top bottom */ @@ -33,10 +33,3 @@ typealias CompactBoxData = List @Serializable class BoxData(val margin: CompactBoxData, val border: CompactBoxData, val padding: CompactBoxData) - -@Serializable -enum class AdditionalDataStatus { - ENABLED, - DISABLED, - NOT_AVAILABLE -} From 173134d31ed16e0dfa02e22e05731aebf30ccacb Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Wed, 29 May 2024 05:29:57 -0700 Subject: [PATCH 42/92] Plugin memory warning 1/n Summary: Added a warning on top of plugin list when queued message consumption passes a threshold. This is designed to make users aware of the resource use of background plugins Reviewed By: antonk52 Differential Revision: D57904618 fbshipit-source-id: b5514826c53c477407f1b1f13350c874d884d267 --- desktop/flipper-ui/src/Client.tsx | 8 ++- .../src/reducers/pluginMessageQueue.tsx | 2 + .../sandy-chrome/appinspect/PluginList.tsx | 2 + .../appinspect/PluginMemoryWarning.tsx | 64 +++++++++++++++++++ .../__tests__/messageQueueSandy.node.tsx | 12 +++- .../src/utils/__tests__/pluginUtils.node.tsx | 2 +- 6 files changed, 85 insertions(+), 5 deletions(-) create mode 100644 desktop/flipper-ui/src/sandy-chrome/appinspect/PluginMemoryWarning.tsx diff --git a/desktop/flipper-ui/src/Client.tsx b/desktop/flipper-ui/src/Client.tsx index d895829ca93..0dd22f3d920 100644 --- a/desktop/flipper-ui/src/Client.tsx +++ b/desktop/flipper-ui/src/Client.tsx @@ -129,7 +129,7 @@ export default class Client extends EventEmitter { string /*pluginKey*/, { plugin: _SandyPluginInstance; - messages: Params[]; + messages: (Params & {rawSize: number})[]; } > = {}; @@ -396,8 +396,12 @@ export default class Client extends EventEmitter { if (!data.params) { throw new Error('expected params'); } - const params: Params = data.params; + const bytes = msg.length * 2; // string lengths are measured in UTF-16 units (not characters), so 2 bytes per char + const params: Params & {rawSize: number} = { + ...data.params, + rawSize: bytes, + }; this.emit('bytes-received', params.api, bytes); if (bytes > 5 * 1024 * 1024) { console.warn( diff --git a/desktop/flipper-ui/src/reducers/pluginMessageQueue.tsx b/desktop/flipper-ui/src/reducers/pluginMessageQueue.tsx index f41ca77da89..eb6cf90aecd 100644 --- a/desktop/flipper-ui/src/reducers/pluginMessageQueue.tsx +++ b/desktop/flipper-ui/src/reducers/pluginMessageQueue.tsx @@ -14,6 +14,8 @@ export const DEFAULT_MAX_QUEUE_SIZE = 10000; export type Message = { method: string; + /** raw size of message in bytes */ + rawSize: number; params?: any; }; diff --git a/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginList.tsx b/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginList.tsx index 46ce98f42e2..2fb95f5b1d4 100644 --- a/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginList.tsx +++ b/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginList.tsx @@ -35,6 +35,7 @@ import {reportUsage} from 'flipper-common'; import ConnectivityStatus from './fb-stubs/ConnectivityStatus'; import {useSelector} from 'react-redux'; import {getPluginLists} from '../../selectors/connections'; +import {PluginMemoryWarning} from './PluginMemoryWarning'; const {SubMenu} = Menu; const {Text} = Typography; @@ -201,6 +202,7 @@ export const PluginList = memo(function PluginList({ connections.selectedPlugin ? [connections.selectedPlugin] : [] } mode="inline"> + {allEnabledPlugins} diff --git a/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginMemoryWarning.tsx b/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginMemoryWarning.tsx new file mode 100644 index 00000000000..5d80f5fc7cb --- /dev/null +++ b/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginMemoryWarning.tsx @@ -0,0 +1,64 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {WarningOutlined} from '@ant-design/icons'; +import {Button} from 'antd'; +import {Layout, theme} from 'flipper-plugin'; +import {getStore} from 'flipper-ui/src/store'; +import React, {useEffect, useState} from 'react'; + +const PluginQueueMemoryUsageScanInterval = 2500; + +export function PluginMemoryWarning() { + const [_, rerender] = useState(0); + + useEffect(() => { + const handle = setInterval(() => { + rerender((x) => x + 1); + }, PluginQueueMemoryUsageScanInterval); + + return () => { + clearInterval(handle); + }; + }, []); + + const totalSizeMb = getQueuedMessagedConsumption(); + + if (totalSizeMb < 50) { + return null; + } + + const color = totalSizeMb < 150 ? theme.warningColor : theme.errorColor; + + return ( + + + + ); +} + +function getQueuedMessagedConsumption() { + const messageQueues = getStore().getState().pluginMessageQueue; + let totalSize = 0; + for (const queue of Object.values(messageQueues)) { + for (const message of queue) { + totalSize += message.rawSize; + } + } + + const totalSizeMb = totalSize / 1000000; + return totalSizeMb; +} diff --git a/desktop/flipper-ui/src/utils/__tests__/messageQueueSandy.node.tsx b/desktop/flipper-ui/src/utils/__tests__/messageQueueSandy.node.tsx index d1bd7d126d5..fd2fa3fdf32 100644 --- a/desktop/flipper-ui/src/utils/__tests__/messageQueueSandy.node.tsx +++ b/desktop/flipper-ui/src/utils/__tests__/messageQueueSandy.node.tsx @@ -718,7 +718,11 @@ test('queue will be cleaned up when it exceeds maximum size', () => { for (i = 0; i < queueSize; i++) { state = pluginMessageQueue( state, - queueMessages(pluginKey, [{method: 'test', params: {i}}], queueSize), + queueMessages( + pluginKey, + [{method: 'test', params: {i}, rawSize: 10}], + queueSize, + ), ); } // almost full @@ -731,7 +735,11 @@ test('queue will be cleaned up when it exceeds maximum size', () => { state = pluginMessageQueue( state, - queueMessages(pluginKey, [{method: 'test', params: {i: ++i}}], queueSize), + queueMessages( + pluginKey, + [{method: 'test', params: {i: ++i}, rawSize: 10}], + queueSize, + ), ); const newLength = Math.ceil(0.9 * queueSize) + 1; // ~4500 diff --git a/desktop/flipper-ui/src/utils/__tests__/pluginUtils.node.tsx b/desktop/flipper-ui/src/utils/__tests__/pluginUtils.node.tsx index cb692db8ac4..49c4bc4fccd 100644 --- a/desktop/flipper-ui/src/utils/__tests__/pluginUtils.node.tsx +++ b/desktop/flipper-ui/src/utils/__tests__/pluginUtils.node.tsx @@ -119,7 +119,7 @@ test('getActivePersistentPlugins, with message queue', async () => { state.pluginMessageQueue = { [getPluginKey(client.id, device, 'ClientPlugin3')]: [ - {method: 'msg', params: {msg: 'ClientPlugin3'}}, + {method: 'msg', params: {msg: 'ClientPlugin3'}, rawSize: 10}, ], }; From a6a705961a062774faba65b7c6f270364eb04c4f Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Wed, 29 May 2024 05:29:57 -0700 Subject: [PATCH 43/92] Plugin memory modal 2/n Summary: Made the warning button interactive with a full breakdown and the ability to deactivate plugins Reviewed By: antonk52 Differential Revision: D57904617 fbshipit-source-id: 341059c2a5d615b12f73abc3fa950c8dd3b73794 --- .../appinspect/PluginMemoryWarning.tsx | 169 +++++++++++++++++- 1 file changed, 168 insertions(+), 1 deletion(-) diff --git a/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginMemoryWarning.tsx b/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginMemoryWarning.tsx index 5d80f5fc7cb..600ae341d8a 100644 --- a/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginMemoryWarning.tsx +++ b/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginMemoryWarning.tsx @@ -8,16 +8,23 @@ */ import {WarningOutlined} from '@ant-design/icons'; -import {Button} from 'antd'; +import {Button, Modal, Table, Typography, TableColumnType} from 'antd'; import {Layout, theme} from 'flipper-plugin'; +import Client from 'flipper-ui/src/Client'; +import {getPluginKey} from 'flipper-ui/src/deprecated-exports'; +import {PluginDefinition} from 'flipper-ui/src/plugin'; +import {switchPlugin} from 'flipper-ui/src/reducers/pluginManager'; import {getStore} from 'flipper-ui/src/store'; import React, {useEffect, useState} from 'react'; +import {useDispatch} from '../../utils/useStore'; +import {Dispatch} from 'redux'; const PluginQueueMemoryUsageScanInterval = 2500; export function PluginMemoryWarning() { const [_, rerender] = useState(0); + const [isModalOpen, setIsModalOpen] = useState(false); useEffect(() => { const handle = setInterval(() => { rerender((x) => x + 1); @@ -41,11 +48,147 @@ export function PluginMemoryWarning() { + + {isModalOpen && ( + setIsModalOpen(false)} + width="100%" + footer={null} + style={{ + top: '5vh', + }}> + rerender((x) => x + 1)} /> + + )} + + ); +} + +function columns( + rerender: () => void, + dispatch: Dispatch, +): TableColumnType[] { + return [ + { + title: 'Plugin name', + dataIndex: 'name', + key: 'name', + render: (value) => {value}, + }, + { + title: 'Application', + dataIndex: 'app', + key: 'app', + }, + + { + title: 'Device', + dataIndex: 'device', + key: 'device', + }, + + { + title: 'Queued messages count', + dataIndex: 'count', + key: 'count', + }, + { + title: 'Queued messages consumption (Mb)', + dataIndex: 'messagesmb', + key: 'messagesmb', + defaultSortOrder: 'descend', + sorter: (a, b) => a.messagesmb - b.messagesmb, + render: (value) => value.toFixed(0), + }, + { + title: 'Actions', + dataIndex: 'actions', + key: 'actions', + render: (_, record) => { + return ( + + ); + }, + }, + ]; +} +type PluginMemoryStats = { + name: string; + app: string; + count: number; + device: string; + messagesmb: number; + pluginId: string; + pluginDef: PluginDefinition; +}; +function PluginMemoryDetails({rerender}: {rerender: () => void}) { + const clients = getStore().getState().connections.clients; + const pluginQueue = getStore().getState().pluginMessageQueue; + const dispatch = useDispatch(); + + const pluginStats = Object.keys(pluginQueue).map((pluginKey) => { + const [pluginDef, client] = matchPluginKeyToClient(pluginKey, clients) ?? [ + null, + null, + ]; + + return { + pluginId: pluginDef?.id, + name: pluginDef?.title ?? pluginDef?.id, + app: client?.query.app ?? 'Unknown', + count: pluginQueue[pluginKey].length, + pluginDef, + device: client?.query.device ?? 'Unknown', + messagesmb: + pluginQueue[pluginKey].reduce((acc, value) => value.rawSize + acc, 0) / + 1000000, + } as PluginMemoryStats; + }); + + console.log('rendeing modal'); + + return ( + + + Background plugin memory usage + +
+ + Background plugins do not consume messages untill you select them in the + UI, they are buffered in memory instead. +
To free up memory, you can deactivate plugins you do not need in + this session. +
+
+
+ ); } @@ -62,3 +205,27 @@ function getQueuedMessagedConsumption() { const totalSizeMb = totalSize / 1000000; return totalSizeMb; } +function matchPluginKeyToClient( + pluginKey: string, + clients: Map, +): [PluginDefinition, Client] | null { + for (const client of clients.values()) { + for (const plugin of [ + ...client.plugins.values(), + ...client.backgroundPlugins.values(), + ]) { + const candidateKey = getPluginKey( + client.id, + {serial: client.query.device_id}, + plugin, + ); + if (candidateKey === pluginKey) { + const pluginDef = [ + ...getStore().getState().plugins.clientPlugins.values(), + ].find((pluginDef) => pluginDef.id === plugin); + return ([pluginDef, client] as [PluginDefinition, Client]) ?? null; + } + } + } + return null; +} From 5ba9541f16b5d3ea152c4240b553316fd27087cb Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Wed, 29 May 2024 05:29:57 -0700 Subject: [PATCH 44/92] Improvements to memory modal 3/n Summary: 1. Fixed bug where modal would disappear if you cleared more data than the lower threshold. 2. Added total mb usage to top of table 3. Added button to clear plugin queue rather tahn deactivate Reviewed By: antonk52 Differential Revision: D57905752 fbshipit-source-id: 81da086c0dc34ae63bbee642a0a69b173d23294e --- .../appinspect/PluginMemoryWarning.tsx | 66 ++++++++++++++----- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginMemoryWarning.tsx b/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginMemoryWarning.tsx index 600ae341d8a..dffab5d5637 100644 --- a/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginMemoryWarning.tsx +++ b/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginMemoryWarning.tsx @@ -18,6 +18,7 @@ import {getStore} from 'flipper-ui/src/store'; import React, {useEffect, useState} from 'react'; import {useDispatch} from '../../utils/useStore'; import {Dispatch} from 'redux'; +import {clearMessageQueue} from 'flipper-ui/src/reducers/pluginMessageQueue'; const PluginQueueMemoryUsageScanInterval = 2500; @@ -37,7 +38,7 @@ export function PluginMemoryWarning() { const totalSizeMb = getQueuedMessagedConsumption(); - if (totalSizeMb < 50) { + if (totalSizeMb < 50 && !isModalOpen) { return null; } @@ -67,7 +68,10 @@ export function PluginMemoryWarning() { style={{ top: '5vh', }}> - rerender((x) => x + 1)} /> + rerender((x) => x + 1)} + /> )} @@ -116,19 +120,34 @@ function columns( key: 'actions', render: (_, record) => { return ( - + + + + ); }, }, @@ -142,8 +161,15 @@ type PluginMemoryStats = { messagesmb: number; pluginId: string; pluginDef: PluginDefinition; + client: Client; }; -function PluginMemoryDetails({rerender}: {rerender: () => void}) { +function PluginMemoryDetails({ + rerender, + totalMb, +}: { + totalMb: number; + rerender: () => void; +}) { const clients = getStore().getState().connections.clients; const pluginQueue = getStore().getState().pluginMessageQueue; const dispatch = useDispatch(); @@ -158,6 +184,7 @@ function PluginMemoryDetails({rerender}: {rerender: () => void}) { pluginId: pluginDef?.id, name: pluginDef?.title ?? pluginDef?.id, app: client?.query.app ?? 'Unknown', + client, count: pluginQueue[pluginKey].length, pluginDef, device: client?.query.device ?? 'Unknown', @@ -180,7 +207,12 @@ function PluginMemoryDetails({rerender}: {rerender: () => void}) { Background plugins do not consume messages untill you select them in the UI, they are buffered in memory instead.
To free up memory, you can deactivate plugins you do not need in - this session. + this session. Alternatively you can purge a plugins message queue + without deactivating it. +
+
+ Total usage:{' '} + {totalMb.toFixed(0)}Mb

From 590b6a093210fedd3d05cb9b9034a6521e52475e Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Wed, 29 May 2024 06:28:24 -0700 Subject: [PATCH 45/92] remove proxy component for launching virtual devices Reviewed By: LukeDefeo Differential Revision: D57885713 fbshipit-source-id: 576d30cf614d192cb85ad92d6434de51a949880d --- .../src/sandy-chrome/appinspect/LaunchEmulator.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/desktop/flipper-ui/src/sandy-chrome/appinspect/LaunchEmulator.tsx b/desktop/flipper-ui/src/sandy-chrome/appinspect/LaunchEmulator.tsx index b6ccce296b5..ce0264d3974 100644 --- a/desktop/flipper-ui/src/sandy-chrome/appinspect/LaunchEmulator.tsx +++ b/desktop/flipper-ui/src/sandy-chrome/appinspect/LaunchEmulator.tsx @@ -40,15 +40,11 @@ const COLD_BOOT = 'cold-boot'; export function showEmulatorLauncher(store: Store) { renderReactRoot((unmount) => ( - + ; )); } -function LaunchEmulatorContainer({onClose}: {onClose: () => void}) { - return ; -} - function NoSDKsEnabledAlert({onClose}: {onClose: () => void}) { const [showSettings, setShowSettings] = useState(false); const footer = ( From 91f0cbb056e4f52e6fb99f4864c89b720736859b Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy Date: Wed, 29 May 2024 06:28:24 -0700 Subject: [PATCH 46/92] human error for empty ios devices in device launcher Summary: Currently we only note that device are not found. We know of two reasons why this can happen. Let's surface them to users. This diff surfaces these reasons to users and extracts messages into a separate component. Reviewed By: LukeDefeo Differential Revision: D57885649 fbshipit-source-id: 832b3cf648c03e4e97d6d7e4e3c8b6cf18305ee7 --- .../appinspect/LaunchEmulator.tsx | 54 ++++++++++++++++--- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/desktop/flipper-ui/src/sandy-chrome/appinspect/LaunchEmulator.tsx b/desktop/flipper-ui/src/sandy-chrome/appinspect/LaunchEmulator.tsx index ce0264d3974..72a4f787e1b 100644 --- a/desktop/flipper-ui/src/sandy-chrome/appinspect/LaunchEmulator.tsx +++ b/desktop/flipper-ui/src/sandy-chrome/appinspect/LaunchEmulator.tsx @@ -78,6 +78,11 @@ function NoSDKsEnabledAlert({onClose}: {onClose: () => void}) { ); } +type IOSState = { + type: 'loading' | 'error' | 'ready' | 'empty'; + message?: string; +}; + export const LaunchEmulatorDialog = withTrackingScope( function LaunchEmulatorDialog({onClose}: {onClose: () => void}) { const iosEnabled = useStore((state) => state.settingsState.enableIOS); @@ -90,7 +95,7 @@ export const LaunchEmulatorDialog = withTrackingScope( const [waitingForIos, setWaitingForIos] = useState(iosEnabled); const [waitingForAndroid, setWaitingForAndroid] = useState(androidEnabled); - const [iOSMessage, setiOSMessage] = useState('Loading...'); + const [iOSMessage, setiOSMessage] = useState({type: 'loading'}); const [androidMessage, setAndroidMessage] = useState('Loading...'); const [favoriteVirtualDevices, setFavoriteVirtualDevices] = @@ -124,11 +129,14 @@ export const LaunchEmulatorDialog = withTrackingScope( setWaitingForIos(false); setIosEmulators(nonPhysical); if (nonPhysical.length === 0) { - setiOSMessage('No simulators found'); + setiOSMessage({type: 'empty'}); } } catch (error) { console.warn('Failed to find iOS simulators', error); - setiOSMessage(`Error: ${error.message ?? error} \nRetrying...`); + setiOSMessage({ + type: 'error', + message: `Error: ${error.message ?? error} \nRetrying...`, + }); setTimeout(getiOSSimulators, 1000); } }; @@ -245,9 +253,7 @@ export const LaunchEmulatorDialog = withTrackingScope( items.push( , iosEmulators.length == 0 ? ( - <Typography.Paragraph style={{textAlign: 'center'}}> - {iOSMessage} - </Typography.Paragraph> + <IOSPlaceholder kind={iOSMessage.type} message={iOSMessage.message} /> ) : null, ...chain(iosEmulators) .map((device) => ({ @@ -335,6 +341,42 @@ export const LaunchEmulatorDialog = withTrackingScope( }, ); +function IOSPlaceholder({ + kind, + message, +}: { + kind: IOSState['type']; + message: string | undefined; +}) { + switch (kind) { + case 'error': + return ( + <Typography.Paragraph style={{textAlign: 'center'}}> + {message} + </Typography.Paragraph> + ); + case 'empty': + return ( + <> + <Typography.Paragraph style={{textAlign: 'center'}}> + No iOS simulators found. This is likely because because{' '} + <code>xcode-select</code> is pointing at a wrong Xcode installation. + See setup doctor for help. Run{' '} + <code>sudo xcode-select -switch /Applications/Xcode_xxxxx.app</code>{' '} + to select the correct Xcode installation (you need to update path to + Xcode.app in the command). + </Typography.Paragraph> + <Typography.Paragraph style={{textAlign: 'center'}}> + Alternatevely, Simulator app may not have any simulators created. + {message} + </Typography.Paragraph> + </> + ); + default: + return null; + } +} + const FavIconStyle = {fontSize: 16, color: theme.primaryColor}; function Title({name}: {name: string}) { From 73a0175e60aa2fbaa1ecedbf5e7ed0839669e398 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy <antonk52@meta.com> Date: Wed, 29 May 2024 06:28:24 -0700 Subject: [PATCH 47/92] propogate timeout error to user Summary: First time launching an iOS simulator will timeout as it can take up to 2 minutes * propagate the error from the server back to client * display a specific error when an idb was killed due to a timeout (30 seconds by default) Reviewed By: lblasa Differential Revision: D57908262 fbshipit-source-id: a95bf0fb7ed334217575de4a9aaf0ef64658d968 --- .../src/devices/ios/iOSDeviceManager.tsx | 7 ++++++- .../appinspect/LaunchEmulator.tsx | 20 +++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/desktop/flipper-server/src/devices/ios/iOSDeviceManager.tsx b/desktop/flipper-server/src/devices/ios/iOSDeviceManager.tsx index 9744f887af4..45f53277111 100644 --- a/desktop/flipper-server/src/devices/ios/iOSDeviceManager.tsx +++ b/desktop/flipper-server/src/devices/ios/iOSDeviceManager.tsx @@ -198,7 +198,12 @@ export class IOSDeviceManager { const bridge = await this.getBridge(); await bridge.launchSimulator(udid); } catch (e) { - console.warn('Failed to launch simulator:', e); + if (e.killed === true && e.signal === 'SIGTERM') { + throw new Error('Failed to launch simulator: command timeout'); + } else { + console.warn('Failed to launch simulator:', e); + throw e; + } } } diff --git a/desktop/flipper-ui/src/sandy-chrome/appinspect/LaunchEmulator.tsx b/desktop/flipper-ui/src/sandy-chrome/appinspect/LaunchEmulator.tsx index 72a4f787e1b..94e79da7a0f 100644 --- a/desktop/flipper-ui/src/sandy-chrome/appinspect/LaunchEmulator.tsx +++ b/desktop/flipper-ui/src/sandy-chrome/appinspect/LaunchEmulator.tsx @@ -286,8 +286,24 @@ export const LaunchEmulatorDialog = withTrackingScope( ); onClose(); } catch (e) { - console.warn('Failed to start simulator: ', e); - message.error(`Failed to start simulator: ${e}`); + if ( + // definitely a server error + typeof e === 'string' && + e.includes('command timeout') + ) { + message.warn( + 'Launching simulator may take up to 2 minutes for the first time. Please wait.', + // seconds + 20, + ); + } else { + console.warn('Failed to start simulator: ', e); + message.error( + `Failed to start simulator: ${e}`, + // seconds + 20, + ); + } } finally { setPendingEmulators( produce((draft) => { From 52663884c0a51e2df7474c6b20202f267ea0acae Mon Sep 17 00:00:00 2001 From: generatedunixname89002005306973 <generatedunixname89002005306973@meta.com> Date: Thu, 30 May 2024 02:24:43 -0700 Subject: [PATCH 48/92] Flipper Release: v0.254.0 Summary: Releasing version 0.254.0 Reviewed By: mweststrate Differential Revision: D57926501 fbshipit-source-id: 1a83874b2df3592a97b44bb613dd2c7309a6f09b --- desktop/package.json | 2 +- desktop/plugins/public/layout/docs/setup.mdx | 2 +- desktop/plugins/public/leak_canary/docs/setup.mdx | 2 +- desktop/plugins/public/network/docs/setup.mdx | 2 +- docs/getting-started/android-native.mdx | 4 ++-- docs/getting-started/react-native-ios.mdx | 2 +- docs/getting-started/react-native.mdx | 4 ++-- gradle.properties | 2 +- js/js-flipper/package.json | 2 +- react-native/react-native-flipper/package.json | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/desktop/package.json b/desktop/package.json index 482f25dc3f1..3a9387c4bd5 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -164,7 +164,7 @@ "npm": "use yarn instead", "yarn": "^1.16" }, - "version": "0.253.0", + "version": "0.254.0", "workspaces": { "packages": [ "scripts", diff --git a/desktop/plugins/public/layout/docs/setup.mdx b/desktop/plugins/public/layout/docs/setup.mdx index 2b1f81d920f..95c7991e77b 100644 --- a/desktop/plugins/public/layout/docs/setup.mdx +++ b/desktop/plugins/public/layout/docs/setup.mdx @@ -27,7 +27,7 @@ You also need to compile in the `litho-annotations` package, as Flipper reflects ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.253.0' + debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.254.0' debugImplementation 'com.facebook.litho:litho-annotations:0.19.0' // ... } diff --git a/desktop/plugins/public/leak_canary/docs/setup.mdx b/desktop/plugins/public/leak_canary/docs/setup.mdx index ab56c22656c..6ec4c30eda4 100644 --- a/desktop/plugins/public/leak_canary/docs/setup.mdx +++ b/desktop/plugins/public/leak_canary/docs/setup.mdx @@ -8,7 +8,7 @@ To setup the <Link to={useBaseUrl("/docs/features/plugins/leak-canary")}>LeakCan ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.253.0' + debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.254.0' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1' } ``` diff --git a/desktop/plugins/public/network/docs/setup.mdx b/desktop/plugins/public/network/docs/setup.mdx index c3c065c3f07..a4f3c7dbc4f 100644 --- a/desktop/plugins/public/network/docs/setup.mdx +++ b/desktop/plugins/public/network/docs/setup.mdx @@ -11,7 +11,7 @@ The network plugin is shipped as a separate Maven artifact, as follows: ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.253.0' + debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.254.0' } ``` diff --git a/docs/getting-started/android-native.mdx b/docs/getting-started/android-native.mdx index a553fa26005..cbdcf72cff0 100644 --- a/docs/getting-started/android-native.mdx +++ b/docs/getting-started/android-native.mdx @@ -24,10 +24,10 @@ repositories { } dependencies { - debugImplementation 'com.facebook.flipper:flipper:0.253.0' + debugImplementation 'com.facebook.flipper:flipper:0.254.0' debugImplementation 'com.facebook.soloader:soloader:0.10.5' - releaseImplementation 'com.facebook.flipper:flipper-noop:0.253.0' + releaseImplementation 'com.facebook.flipper:flipper-noop:0.254.0' } ``` diff --git a/docs/getting-started/react-native-ios.mdx b/docs/getting-started/react-native-ios.mdx index 14af6750b4b..273037d14c7 100644 --- a/docs/getting-started/react-native-ios.mdx +++ b/docs/getting-started/react-native-ios.mdx @@ -51,7 +51,7 @@ Add all of the code below to your `ios/Podfile`: platform :ios, '9.0' def flipper_pods() - flipperkit_version = '0.253.0' # should match the version of your Flipper client app + flipperkit_version = '0.254.0' # should match the version of your Flipper client app pod 'FlipperKit', '~>' + flipperkit_version, :configuration => 'Debug' pod 'FlipperKit/FlipperKitLayoutPlugin', '~>' + flipperkit_version, :configuration => 'Debug' pod 'FlipperKit/SKIOSNetworkPlugin', '~>' + flipperkit_version, :configuration => 'Debug' diff --git a/docs/getting-started/react-native.mdx b/docs/getting-started/react-native.mdx index b98b66f1f16..ca3f349f141 100644 --- a/docs/getting-started/react-native.mdx +++ b/docs/getting-started/react-native.mdx @@ -34,7 +34,7 @@ Latest version of Flipper requires react-native 0.69+! If you use react-native < Android: -1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.253.0`. +1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.254.0`. 2. Run `./gradlew clean` in the `android` directory. iOS: @@ -44,7 +44,7 @@ react-native version => 0.69.0 2. Run `pod install --repo-update` in the `ios` directory. react-native version < 0.69.0 -1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.253.0' })`. +1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.254.0' })`. 2. Run `pod install --repo-update` in the `ios` directory. ## Manual Setup diff --git a/gradle.properties b/gradle.properties index 55d1344811d..3df80e38274 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. # POM publishing constants -VERSION_NAME=0.253.1-SNAPSHOT +VERSION_NAME=0.254.0 GROUP=com.facebook.flipper SONATYPE_STAGING_PROFILE=comfacebook POM_URL=https://github.com/facebook/flipper diff --git a/js/js-flipper/package.json b/js/js-flipper/package.json index f315a762f2f..61d6e8af8ce 100644 --- a/js/js-flipper/package.json +++ b/js/js-flipper/package.json @@ -1,7 +1,7 @@ { "name": "js-flipper", "title": "JS Flipper Bindings for Web-Socket based clients", - "version": "0.253.0", + "version": "0.254.0", "main": "lib/index.js", "browser": { "os": false diff --git a/react-native/react-native-flipper/package.json b/react-native/react-native-flipper/package.json index 3ee61c94e4c..559c03bf3fd 100644 --- a/react-native/react-native-flipper/package.json +++ b/react-native/react-native-flipper/package.json @@ -1,7 +1,7 @@ { "name": "react-native-flipper", "title": "React Native Flipper Bindings", - "version": "0.253.0", + "version": "0.254.0", "description": "Flipper bindings for React Native", "main": "index.js", "types": "index.d.ts", From f4b0f3bb66ac52b5ac4156b950b51b1069a5733b Mon Sep 17 00:00:00 2001 From: generatedunixname89002005306973 <generatedunixname89002005306973@meta.com> Date: Thu, 30 May 2024 02:24:43 -0700 Subject: [PATCH 49/92] Flipper Snapshot Bump: v0.254.1-SNAPSHOT Summary: Releasing snapshot version 0.254.1-SNAPSHOT Reviewed By: mweststrate Differential Revision: D57926500 fbshipit-source-id: b98cd37cceb686322dd401c6224499bf6dde6722 --- docs/getting-started/android-native.mdx | 4 ++-- gradle.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/android-native.mdx b/docs/getting-started/android-native.mdx index cbdcf72cff0..8961f18e0ea 100644 --- a/docs/getting-started/android-native.mdx +++ b/docs/getting-started/android-native.mdx @@ -124,10 +124,10 @@ repositories { } dependencies { - debugImplementation 'com.facebook.flipper:flipper:0.253.1-SNAPSHOT' + debugImplementation 'com.facebook.flipper:flipper:0.254.1-SNAPSHOT' debugImplementation 'com.facebook.soloader:soloader:0.10.5' - releaseImplementation 'com.facebook.flipper:flipper-noop:0.253.1-SNAPSHOT' + releaseImplementation 'com.facebook.flipper:flipper-noop:0.254.1-SNAPSHOT' } ``` diff --git a/gradle.properties b/gradle.properties index 3df80e38274..cb90622de76 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. # POM publishing constants -VERSION_NAME=0.254.0 +VERSION_NAME=0.254.1-SNAPSHOT GROUP=com.facebook.flipper SONATYPE_STAGING_PROFILE=comfacebook POM_URL=https://github.com/facebook/flipper From 5a0a76a59fa9a17d512857b43f5329f37a177cd6 Mon Sep 17 00:00:00 2001 From: Luke De Feo <lukedefeo@meta.com> Date: Thu, 30 May 2024 04:49:29 -0700 Subject: [PATCH 50/92] Add UI for additional data collection Summary: A new feature was added on a node level, additional data collection. here is how it works First the client indicates that a particular node has additional expensive data it can send If flipper sees the flag then it displays a ui for the user to opt in or out flipper sends message to client to add current node id to be opted into additional data collection, this is kept in a set on the client, on the next traversal and subsequent traversals while the node id of the coposable is in the set the additional data collection is performed Reviewed By: antonk52 Differential Revision: D57868531 fbshipit-source-id: 4d901bca3f74a6b01a0e541dc858efa38adc1902 --- .../plugins/public/ui-debugger/ClientTypes.tsx | 5 +++++ .../attributes/AttributesInspector.tsx | 17 +++++++++++++++++ desktop/plugins/public/ui-debugger/index.tsx | 9 +++++++++ 3 files changed, 31 insertions(+) diff --git a/desktop/plugins/public/ui-debugger/ClientTypes.tsx b/desktop/plugins/public/ui-debugger/ClientTypes.tsx index 1d3c40d11dc..e9b5ccb73a3 100644 --- a/desktop/plugins/public/ui-debugger/ClientTypes.tsx +++ b/desktop/plugins/public/ui-debugger/ClientTypes.tsx @@ -32,6 +32,10 @@ export type Methods = { customActionIndex: number; value: T; }): Promise<{result: T}>; + additionalNodeInspectionChange(params: { + changeType: 'Add' | 'Remove'; + nodeId: Id; + }): Promise<void>; }; export type CompoundTypeHint = @@ -188,6 +192,7 @@ export type ClientNode = { bounds: Bounds; tags: Tag[]; activeChild?: Id; + additionalDataCollection?: boolean; }; /** diff --git a/desktop/plugins/public/ui-debugger/components/sidebarV2/attributes/AttributesInspector.tsx b/desktop/plugins/public/ui-debugger/components/sidebarV2/attributes/AttributesInspector.tsx index e48c4ab58b2..1456e84c024 100644 --- a/desktop/plugins/public/ui-debugger/components/sidebarV2/attributes/AttributesInspector.tsx +++ b/desktop/plugins/public/ui-debugger/components/sidebarV2/attributes/AttributesInspector.tsx @@ -53,6 +53,7 @@ import { import {StyledTextArea} from './TextInput'; import {ColorInspector} from './ColorInput'; import {SelectInput} from './SelectInput'; +import {MultiSelectableDropDownItem} from '../../shared/MultiSelectableDropDownItem'; type ModalData = { data: unknown; @@ -77,6 +78,7 @@ export function AttributesInspector({ node: ClientNode; metadata: MetadataMap; }) { + const instance = usePlugin(plugin); const [modalData, setModalData] = useState<ModalData | null>(null); const [attributeFilter, setAttributeFilter] = useLocalStorageState( @@ -143,6 +145,21 @@ export function AttributesInspector({ placeholder="Filter attributes" prefix={<SearchOutlined />} /> + {node.additionalDataCollection != null && ( + <MultiSelectableDropDownItem + text="Collect additional data" + value={node.id} + selectedValues={ + new Set(node.additionalDataCollection ? [node.id] : []) + } + onSelect={(_, selected) => + instance.onAdditionalDataCollectionChanged( + node.id, + selected ? 'Add' : 'Remove', + ) + } + /> + )} {sections.length === 0 ? ( <NoData message="No attributes match filter " /> diff --git a/desktop/plugins/public/ui-debugger/index.tsx b/desktop/plugins/public/ui-debugger/index.tsx index a2850c2e366..24418a07495 100644 --- a/desktop/plugins/public/ui-debugger/index.tsx +++ b/desktop/plugins/public/ui-debugger/index.tsx @@ -308,9 +308,18 @@ export function plugin(client: PluginClient<Events, Methods>) { console.warn('onCustomAction failed', e); } } + + async function onAdditionalDataCollectionChanged( + nodeId: Id, + changeType: 'Add' | 'Remove', + ) { + client.send('additionalNodeInspectionChange', {nodeId, changeType}); + } + return { rootId, customActionGroups, + onAdditionalDataCollectionChanged, onCustomAction, currentFrameTime: lastProcessedFrameTime as _ReadOnlyAtom<number>, uiState: uiState as ReadOnlyUIState, From 9df66429add6b06ae0b031fdbb2271cd98103101 Mon Sep 17 00:00:00 2001 From: Fabio Carballo <fabiocarballo@meta.com> Date: Mon, 3 Jun 2024 02:04:35 -0700 Subject: [PATCH 51/92] Fix for fabio Summary: This activity has no content so ignoring it shows the bottom sheet Reviewed By: zielinskimz Differential Revision: D57969020 fbshipit-source-id: 5f5af8ce053c8fe2439da90cfc6357517074f782 --- .../plugins/uidebugger/descriptors/ApplicationRefDescriptor.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/ApplicationRefDescriptor.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/ApplicationRefDescriptor.kt index a2cf85c9478..ededb39477e 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/ApplicationRefDescriptor.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/ApplicationRefDescriptor.kt @@ -61,7 +61,8 @@ object ApplicationRefDescriptor : ChainedDescriptor<ApplicationRef>() { fun isUsefulRoot(rootViewOrActivity: Any): Boolean { val className = rootViewOrActivity.javaClass.name - if (className.contains("mediagallery.ui.MediaGalleryActivity")) { + if (className.contains("mediagallery.ui.MediaGalleryActivity") || + className.contains("ImagineCreationActivity")) { // this activity doesn't contain the content and its actually in the decor view behind it, so // skip it :/ return false From b6e26b035a3183b4d503e6172b37d1f3dbfc24eb Mon Sep 17 00:00:00 2001 From: Luke De Feo <lukedefeo@meta.com> Date: Mon, 3 Jun 2024 08:43:38 -0700 Subject: [PATCH 52/92] Support parsing json bodys with numbers that are > 2^53 Summary: Context https://fb.workplace.com/groups/flipper.community.support/permalink/1005276087877573/ Since network plugin sends json bodys as raw json blobs we can deserialise it with a library that supports big int. This allows us to show numbers in the way native clients see it. This will not work for headers since they are send as part of the main flipper message which is parsed by the browser and does not support big int whilst the added library carrys a perf impact we only pay the cost when the user clicks and we render the sidebar Reviewed By: passy Differential Revision: D57780934 fbshipit-source-id: 622d3f99248d984538cbbd91da2f8c527e1cd5c8 --- desktop/plugins/public/network/RequestDetails.tsx | 9 ++++++--- desktop/plugins/public/network/package.json | 3 ++- desktop/plugins/public/network/utils.tsx | 9 +++++++++ desktop/plugins/public/yarn.lock | 5 +++++ 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/desktop/plugins/public/network/RequestDetails.tsx b/desktop/plugins/public/network/RequestDetails.tsx index dd6a1fd4027..2fbe290527e 100644 --- a/desktop/plugins/public/network/RequestDetails.tsx +++ b/desktop/plugins/public/network/RequestDetails.tsx @@ -27,6 +27,7 @@ import { bodyAsString, formatBytes, getHeaderValue, + parseJsonWithBigInt, queryToObj, } from './utils'; import {Request, Header, Insights, RetryInsights} from './types'; @@ -34,6 +35,7 @@ import {BodyOptions} from './index'; import {ProtobufDefinitionsRepository} from './ProtobufDefinitionsRepository'; import {KeyValueItem, KeyValueTable} from './KeyValueTable'; import {CopyOutlined} from '@ant-design/icons'; +import {stringify} from 'lossless-json'; const {Text} = Typography; @@ -406,7 +408,7 @@ class JSONText extends Component<{children: any}> { const jsonObject = this.props.children; return ( <CodeBlock> - {JSON.stringify(jsonObject, null, 2)} + {stringify(jsonObject, null, 2)} {'\n'} </CodeBlock> ); @@ -448,7 +450,7 @@ class JSONTextFormatter { contentType.startsWith('application/x-fb-flatbuffer') ) { try { - const data = JSON.parse(body); + const data = parseJsonWithBigInt(body); return <JSONText>{data}</JSONText>; } catch (SyntaxError) { // Multiple top level JSON roots, map them one by one @@ -612,7 +614,8 @@ class GraphQLFormatter { contentType.startsWith('application/x-fb-flatbuffer') ) { try { - const data = JSON.parse(body); + const data = parseJsonWithBigInt(body); + return ( <div> {this.parsedServerTimeForFirstFlush(data)} diff --git a/desktop/plugins/public/network/package.json b/desktop/plugins/public/network/package.json index 2821132b0ff..2815aca00b8 100644 --- a/desktop/plugins/public/network/package.json +++ b/desktop/plugins/public/network/package.json @@ -21,7 +21,8 @@ "lodash": "^4.17.21", "pako": "^2.0.3", "protobufjs": "^6.10.2", - "xml-beautifier": "^0.4.0" + "xml-beautifier": "^0.4.0", + "lossless-json": "^4.0.1" }, "peerDependencies": { "flipper": "*", diff --git a/desktop/plugins/public/network/utils.tsx b/desktop/plugins/public/network/utils.tsx index a8f689347a3..42c80a02536 100644 --- a/desktop/plugins/public/network/utils.tsx +++ b/desktop/plugins/public/network/utils.tsx @@ -12,6 +12,7 @@ import decompress from 'brotli/decompress'; import pako from 'pako'; import {Request, Header, ResponseInfo} from './types'; import {Base64} from 'js-base64'; +import {isInteger, parse} from 'lossless-json'; export function getHeaderValue( headers: Array<Header> | undefined, @@ -264,6 +265,14 @@ export function formatBytes(count: number | undefined): string { return `${count} B`; } +function customNumberParser(value: string) { + return isInteger(value) ? BigInt(value) : parseFloat(value); +} + +export function parseJsonWithBigInt(jsonStr: string) { + return parse(jsonStr, null, customNumberParser); +} + export function formatOperationName(requestData: string): string { try { const parsedData = JSON.parse(requestData); diff --git a/desktop/plugins/public/yarn.lock b/desktop/plugins/public/yarn.lock index df49edb89f7..82c51ba8878 100644 --- a/desktop/plugins/public/yarn.lock +++ b/desktop/plugins/public/yarn.lock @@ -1529,6 +1529,11 @@ loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +lossless-json@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lossless-json/-/lossless-json-4.0.1.tgz#d45229e3abb213a0235812780ca894ea8c5b2c6b" + integrity sha512-l0L+ppmgPDnb+JGxNLndPtJZGNf6+ZmVaQzoxQm3u6TXmhdnsA+YtdVR8DjzZd/em58686CQhOFDPewfJ4l7MA== + lz-string@^1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" From b93fbbd2f72105954e2a5e2a4fbfe381ab3c873d Mon Sep 17 00:00:00 2001 From: Luke De Feo <lukedefeo@meta.com> Date: Mon, 3 Jun 2024 08:43:38 -0700 Subject: [PATCH 53/92] Store request / response data in indexed db Summary: Request / response data can be quite large. To avoid flipper accumulate lots of memory we shuffle the data to disk and only read it when the user selects a row. changelog: Network plugin: Store data in IndexedDB to reduce memory consumption Reviewed By: antonk52 Differential Revision: D57865621 fbshipit-source-id: ddff62b30373d9c1ca804652569659edf839789c --- .../plugins/public/network/RequestDataDB.tsx | 113 ++++++++++++++++++ .../plugins/public/network/RequestDetails.tsx | 50 ++++---- .../public/network/__tests__/chunks.node.tsx | 12 +- .../network/__tests__/customheaders.node.tsx | 5 +- .../network/__tests__/encoding.node.tsx | 11 +- desktop/plugins/public/network/index.tsx | 88 +++++++++----- desktop/plugins/public/network/package.json | 8 +- .../request-mocking/NetworkRouteManager.tsx | 20 ++-- desktop/plugins/public/network/types.tsx | 8 +- desktop/plugins/public/network/utils.tsx | 22 ++-- desktop/plugins/public/yarn.lock | 20 ++++ desktop/scripts/verify-types-dependencies.tsx | 1 + 12 files changed, 274 insertions(+), 84 deletions(-) create mode 100644 desktop/plugins/public/network/RequestDataDB.tsx diff --git a/desktop/plugins/public/network/RequestDataDB.tsx b/desktop/plugins/public/network/RequestDataDB.tsx new file mode 100644 index 00000000000..d4d31428429 --- /dev/null +++ b/desktop/plugins/public/network/RequestDataDB.tsx @@ -0,0 +1,113 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import {openDB, deleteDB, DBSchema, IDBPDatabase} from 'idb'; +import {Request, RequestWithData} from './types'; +interface RequestDBSchema extends DBSchema { + requests: { + key: string; + value: string | Uint8Array | undefined; + }; + responses: { + key: string; + value: string | Uint8Array | undefined; + }; +} +export type Data = string | Uint8Array | undefined; + +let shouldWipeDB = true; +let instanceId = 0; + +const dbName = 'network-plugin-data'; + +export class RequestDataDB { + private dbPromise: Promise<IDBPDatabase<RequestDBSchema>> | null = null; + private instanceId: string; + constructor() { + this.instanceId = (instanceId++).toString(); + } + private async initializeDB(): Promise<IDBPDatabase<RequestDBSchema>> { + if (this.dbPromise) { + return this.dbPromise; + } + + this.dbPromise = (async () => { + if (shouldWipeDB) { + shouldWipeDB = false; + console.log('[network] Deleting database'); + + try { + await this.deleteDBWithTimeout(); + console.log('[network] Database deleted successfully'); + } catch (e) { + console.warn('[network] Failed to delete database', e); + } + } + return openDB<RequestDBSchema>(dbName, 1, { + upgrade(db) { + db.createObjectStore('requests'); + db.createObjectStore('responses'); + console.log('[network] Created db object stores', dbName); + }, + }); + })(); + return this.dbPromise; + } + deleteDBWithTimeout(timeout = 5000): Promise<void> { + return new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + reject( + new Error( + '[network] Timeout: Unable to delete database, probably due to open connections', + ), + ); + }, timeout); + deleteDB(dbName) + .then(() => { + clearTimeout(timeoutId); + resolve(); + }) + .catch(reject); + }); + } + async storeRequestData(id: string, data: Data) { + const db = await this.initializeDB(); + return db.put('requests', data, id + this.instanceId); + } + async getRequestData(id: string): Promise<Data> { + const db = await this.initializeDB(); + return db.get('requests', id + this.instanceId); + } + async storeResponseData(id: string, data: Data) { + const db = await this.initializeDB(); + return db.put('responses', data, id + this.instanceId); + } + async getResponseData(id: string): Promise<Data> { + const db = await this.initializeDB(); + return db.get('responses', id + this.instanceId); + } + + async closeConnection() { + const db = await this.initializeDB(); + db.close(); + this.dbPromise = null; + console.log( + `[network] Closed Connection to db for instance ${this.instanceId}`, + ); + } + async addDataToRequest(request: Request): Promise<RequestWithData> { + const requestData = await this.getRequestData(request.id); + const responseData = await this.getResponseData(request.id); + return { + ...request, + requestData, + responseData, + }; + } +} diff --git a/desktop/plugins/public/network/RequestDetails.tsx b/desktop/plugins/public/network/RequestDetails.tsx index 2fbe290527e..085ebac8880 100644 --- a/desktop/plugins/public/network/RequestDetails.tsx +++ b/desktop/plugins/public/network/RequestDetails.tsx @@ -30,7 +30,7 @@ import { parseJsonWithBigInt, queryToObj, } from './utils'; -import {Request, Header, Insights, RetryInsights} from './types'; +import {Header, Insights, RetryInsights, RequestWithData} from './types'; import {BodyOptions} from './index'; import {ProtobufDefinitionsRepository} from './ProtobufDefinitionsRepository'; import {KeyValueItem, KeyValueTable} from './KeyValueTable'; @@ -40,7 +40,7 @@ import {stringify} from 'lossless-json'; const {Text} = Typography; type RequestDetailsProps = { - request: Request; + request: RequestWithData; bodyFormat: string; onSelectFormat: (bodyFormat: string) => void; onCopyText(test: string): void; @@ -133,7 +133,7 @@ export default class RequestDetails extends Component<RequestDetailsProps> { typeof request.responseData === 'string' && request.responseData ? ( <CopyOutlined - title="Copy response body" + title="Copy raw response body" onClick={(e) => { e.stopPropagation(); onCopyText(request.responseData as string); @@ -210,12 +210,12 @@ class HeaderInspector extends Component< } type BodyFormatter = { - formatRequest?: (request: Request) => any; - formatResponse?: (request: Request) => any; + formatRequest?: (request: RequestWithData) => any; + formatResponse?: (request: RequestWithData) => any; }; class RequestBodyInspector extends Component<{ - request: Request; + request: RequestWithData; formattedText: boolean; }> { render() { @@ -251,7 +251,7 @@ class RequestBodyInspector extends Component<{ } class ResponseBodyInspector extends Component<{ - request: Request; + request: RequestWithData; formattedText: boolean; }> { render() { @@ -300,7 +300,7 @@ const Empty = () => ( </Layout.Container> ); -function renderRawBody(request: Request, mode: 'request' | 'response') { +function renderRawBody(request: RequestWithData, mode: 'request' | 'response') { const data = mode === 'request' ? request.requestData : request.responseData; return ( <Layout.Container gap> @@ -359,7 +359,7 @@ class ImageWithSize extends Component<ImageWithSizeProps, ImageWithSizeState> { } class ImageFormatter { - formatResponse(request: Request) { + formatResponse(request: RequestWithData) { if ( getHeaderValue(request.responseHeaders, 'content-type').startsWith( 'image/', @@ -389,7 +389,7 @@ class VideoFormatter { maxHeight: 500, }); - formatResponse = (request: Request) => { + formatResponse = (request: RequestWithData) => { const contentType = getHeaderValue(request.responseHeaders, 'content-type'); if (contentType.startsWith('video/')) { return ( @@ -428,14 +428,14 @@ class XMLText extends Component<{body: any}> { } class JSONTextFormatter { - formatRequest(request: Request) { + formatRequest(request: RequestWithData) { return this.format( bodyAsString(request.requestData), getHeaderValue(request.requestHeaders, 'content-type'), ); } - formatResponse(request: Request) { + formatResponse(request: RequestWithData) { return this.format( bodyAsString(request.responseData), getHeaderValue(request.responseHeaders, 'content-type'), @@ -464,14 +464,14 @@ class JSONTextFormatter { } class XMLTextFormatter { - formatRequest(request: Request) { + formatRequest(request: RequestWithData) { return this.format( bodyAsString(request.requestData), getHeaderValue(request.requestHeaders, 'content-type'), ); } - formatResponse(request: Request) { + formatResponse(request: RequestWithData) { return this.format( bodyAsString(request.responseData), getHeaderValue(request.responseHeaders, 'content-type'), @@ -490,14 +490,14 @@ class XMLTextFormatter { } class JSONFormatter { - formatRequest(request: Request) { + formatRequest(request: RequestWithData) { return this.format( bodyAsString(request.requestData), getHeaderValue(request.requestHeaders, 'content-type'), ); } - formatResponse(request: Request) { + formatResponse(request: RequestWithData) { return this.format( bodyAsString(request.responseData), getHeaderValue(request.responseHeaders, 'content-type'), @@ -530,7 +530,7 @@ class JSONFormatter { } class LogEventFormatter { - formatRequest(request: Request) { + formatRequest(request: RequestWithData) { if (request.url.indexOf('logging_client_event') > 0) { const data = queryToObj(bodyAsString(request.requestData)); if (typeof data.message === 'string') { @@ -542,7 +542,7 @@ class LogEventFormatter { } class GraphQLBatchFormatter { - formatRequest(request: Request) { + formatRequest(request: RequestWithData) { if (request.url.indexOf('graphqlbatch') > 0) { const data = queryToObj(bodyAsString(request.requestData)); if (typeof data.queries === 'string') { @@ -579,7 +579,7 @@ class GraphQLFormatter { </Text> ); } - formatRequest(request: Request) { + formatRequest(request: RequestWithData) { if (request.url.indexOf('graphql') > 0) { const decoded = request.requestData; if (!decoded) { @@ -596,7 +596,7 @@ class GraphQLFormatter { } } - formatResponse(request: Request) { + formatResponse(request: RequestWithData) { return this.format( // TODO: Fix this the next time the file is edited. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -641,7 +641,7 @@ class GraphQLFormatter { } class FormUrlencodedFormatter { - formatRequest = (request: Request) => { + formatRequest = (request: RequestWithData) => { const contentType = getHeaderValue(request.requestHeaders, 'content-type'); if (contentType.startsWith('application/x-www-form-urlencoded')) { const decoded = request.requestData; @@ -656,7 +656,7 @@ class FormUrlencodedFormatter { } class BinaryFormatter { - formatRequest(request: Request) { + formatRequest(request: RequestWithData) { if ( getHeaderValue(request.requestHeaders, 'content-type') === 'application/octet-stream' @@ -666,7 +666,7 @@ class BinaryFormatter { return undefined; } - formatResponse(request: Request) { + formatResponse(request: RequestWithData) { if ( getHeaderValue(request.responseHeaders, 'content-type') === 'application/octet-stream' @@ -681,7 +681,7 @@ class ProtobufFormatter { private protobufDefinitionRepository = ProtobufDefinitionsRepository.getInstance(); - formatRequest(request: Request) { + formatRequest(request: RequestWithData) { if ( getHeaderValue(request.requestHeaders, 'content-type') === 'application/x-protobuf' @@ -716,7 +716,7 @@ class ProtobufFormatter { return undefined; } - formatResponse(request: Request) { + formatResponse(request: RequestWithData) { if ( getHeaderValue(request.responseHeaders, 'content-type') === 'application/x-protobuf' || diff --git a/desktop/plugins/public/network/__tests__/chunks.node.tsx b/desktop/plugins/public/network/__tests__/chunks.node.tsx index bdc5bc02491..d8c2fc6189c 100644 --- a/desktop/plugins/public/network/__tests__/chunks.node.tsx +++ b/desktop/plugins/public/network/__tests__/chunks.node.tsx @@ -7,6 +7,8 @@ * @format */ +import 'core-js/stable/structured-clone'; +import 'fake-indexeddb/auto'; import {combineBase64Chunks} from '../chunks'; import {TestUtils, path} from 'flipper-plugin'; import * as NetworkPlugin from '../index'; @@ -84,7 +86,7 @@ test('Reducer correctly adds followup chunk', () => { `); }); -test('Reducer correctly combines initial response and followup chunk', () => { +test('Reducer correctly combines initial response and followup chunk', async () => { const {instance, sendEvent} = TestUtils.startPlugin(NetworkPlugin); sendEvent('newRequest', { data: btoa('x'), @@ -137,7 +139,6 @@ test('Reducer correctly combines initial response and followup chunk', () => { } `); expect(instance.requests.records()[0]).toMatchObject({ - requestData: 'x', requestHeaders: [ {key: 'y', value: 'z'}, { @@ -165,7 +166,6 @@ test('Reducer correctly combines initial response and followup chunk', () => { insights: undefined, method: 'GET', reason: 'nothing', - requestData: 'x', requestHeaders: [ { key: 'y', @@ -176,13 +176,17 @@ test('Reducer correctly combines initial response and followup chunk', () => { value: 'text/plain', }, ], - responseData: 'hello', responseHeaders: [{key: 'Content-Type', value: 'text/plain'}], responseIsMock: false, responseLength: 5, status: '200', url: 'http://test.com', }); + + const persistedRequestData = await instance.db.getRequestData('1'); + expect(persistedRequestData).toEqual('x'); + const persistedResponseData = await instance.db.getResponseData('1'); + expect(persistedResponseData).toEqual('hello'); }); async function readJsonFixture(filename: string) { diff --git a/desktop/plugins/public/network/__tests__/customheaders.node.tsx b/desktop/plugins/public/network/__tests__/customheaders.node.tsx index 0751e478ec2..d8f43d7496f 100644 --- a/desktop/plugins/public/network/__tests__/customheaders.node.tsx +++ b/desktop/plugins/public/network/__tests__/customheaders.node.tsx @@ -7,6 +7,8 @@ * @format */ +import 'core-js/stable/structured-clone'; +import 'fake-indexeddb/auto'; import {TestUtils} from 'flipper-plugin'; import * as NetworkPlugin from '../index'; @@ -118,11 +120,10 @@ test('Can handle custom headers', async () => { }, ]); - renderer.unmount(); - // after import, columns should be visible and restored { const snapshot = await exportStateAsync(); + renderer.unmount(); // Note: snapshot is set in the previous test const {instance: instance2, renderer: renderer2} = TestUtils.renderPlugin( NetworkPlugin, diff --git a/desktop/plugins/public/network/__tests__/encoding.node.tsx b/desktop/plugins/public/network/__tests__/encoding.node.tsx index 2df48b125fb..37916cdca6e 100644 --- a/desktop/plugins/public/network/__tests__/encoding.node.tsx +++ b/desktop/plugins/public/network/__tests__/encoding.node.tsx @@ -7,6 +7,8 @@ * @format */ +import 'core-js/stable/structured-clone'; +import 'fake-indexeddb/auto'; import {readFile} from 'fs'; import {decodeBody, isTextual} from '../utils'; import {ResponseInfo} from '../types'; @@ -195,14 +197,12 @@ test('binary data gets serialized correctly', async () => { value: 'text/plain', }, ], - requestData: donatingExpected, responseHeaders: [ { key: 'Content-Type', value: 'image/png', }, ], - responseData: new Uint8Array(tinyLogoExpected), }); const snapshot = await exportStateAsync(); @@ -255,8 +255,7 @@ test('binary data gets serialized correctly', async () => { value: 'text/plain', }, ], - requestData: donatingExpected, - responseData: new Uint8Array(tinyLogoExpected), + responseHeaders: [ { key: 'Content-Type', @@ -268,4 +267,8 @@ test('binary data gets serialized correctly', async () => { status: '200', url: 'http://www.fbflipper.com', }); + const persistedRequestData2 = await instance2.db.getRequestData('0'); + expect(persistedRequestData2).toEqual(donatingExpected); + const persistedResponseData2 = await instance2.db.getResponseData('0'); + expect(persistedResponseData2).toEqual(new Uint8Array(tinyLogoExpected)); }); diff --git a/desktop/plugins/public/network/index.tsx b/desktop/plugins/public/network/index.tsx index 0060f59b0fc..e46f4955c0c 100644 --- a/desktop/plugins/public/network/index.tsx +++ b/desktop/plugins/public/network/index.tsx @@ -7,7 +7,7 @@ * @format */ -import React, {createRef} from 'react'; +import React, {createRef, useEffect, useState} from 'react'; import { Button, Form, @@ -16,6 +16,7 @@ import { message, Modal, Radio, + Spin, Typography, } from 'antd'; @@ -44,6 +45,7 @@ import { AddProtobufEvent, PartialResponses, SerializedRequest, + RequestWithData, } from './types'; import {ProtobufDefinitionsRepository} from './ProtobufDefinitionsRepository'; import { @@ -55,7 +57,6 @@ import { formatDuration, requestsToText, decodeBody, - formatOperationName, } from './utils'; import RequestDetails from './RequestDetails'; import {assembleChunksIfResponseIsComplete} from './chunks'; @@ -71,6 +72,7 @@ import { computeMockRoutes, } from './request-mocking/NetworkRouteManager'; import {Base64} from 'js-base64'; +import {RequestDataDB} from './RequestDataDB'; const LOCALSTORAGE_MOCK_ROUTE_LIST_KEY = '__NETWORK_CACHED_MOCK_ROUTE_LIST'; const LOCALSTORAGE_RESPONSE_BODY_FORMAT_KEY = @@ -131,6 +133,12 @@ export function plugin(client: PluginClient<Events, Methods>) { }); const columns = createState<DataTableColumn<Request>[]>(baseColumns); // not persistable + const db = new RequestDataDB(); + + client.onDeactivate(() => { + db.closeConnection(); + }); + client.onDeepLink((payload: unknown) => { const searchTermDelim = 'searchTerm='; if (typeof payload !== 'string') { @@ -167,6 +175,7 @@ export function plugin(client: PluginClient<Events, Methods>) { console.warn(`Ignoring duplicate request with id ${data.id}:`, data); } else { requests.append(createRequestFromRequestInfo(data, customColumns.get())); + db.storeRequestData(data.id, decodeBody(data.headers, data.data)); } }); @@ -179,6 +188,10 @@ export function plugin(client: PluginClient<Events, Methods>) { requests.upsert( updateRequestWithResponseInfo(request, response, customColumns.get()), ); + db.storeResponseData( + response.id, + decodeBody(response.headers, response.data), + ); } client.onMessage('newResponse', (data) => { @@ -277,6 +290,7 @@ export function plugin(client: PluginClient<Events, Methods>) { routes, informClientMockChange, tableManagerRef, + db, ), ); } @@ -365,18 +379,19 @@ export function plugin(client: PluginClient<Events, Methods>) { const serializedRequests: SerializedRequest[] = []; for (let i = 0; i < requests.size; i++) { const request = requests.get(i); + const requestWithData = await db.addDataToRequest(request); serializedRequests.push({ ...request, requestTime: request.requestTime.getTime(), responseTime: request.responseTime?.getTime(), requestData: - request.requestData instanceof Uint8Array - ? [Base64.fromUint8Array(request.requestData)] - : request.requestData, + requestWithData.requestData instanceof Uint8Array + ? [Base64.fromUint8Array(requestWithData.requestData)] + : requestWithData.requestData, responseData: - request.responseData instanceof Uint8Array - ? [Base64.fromUint8Array(request.responseData)] - : request.responseData, + requestWithData.responseData instanceof Uint8Array + ? [Base64.fromUint8Array(requestWithData.responseData)] + : requestWithData.responseData, }); if (idler.isCancelled()) { return; @@ -399,6 +414,16 @@ export function plugin(client: PluginClient<Events, Methods>) { isMockResponseSupported.set(data.isMockResponseSupported); customColumns.set(data.customColumns); data.requests2.forEach((request) => { + const requestData = Array.isArray(request.requestData) + ? Base64.toUint8Array(request.requestData[0]) + : request.requestData; + + db.storeRequestData(request.id, requestData); + const responseData = Array.isArray(request.responseData) + ? Base64.toUint8Array(request.responseData[0]) + : request.responseData; + + db.storeResponseData(request.id, responseData); requests.append({ ...request, requestTime: new Date(request.requestTime), @@ -406,17 +431,12 @@ export function plugin(client: PluginClient<Events, Methods>) { request.responseTime != null ? new Date(request.responseTime) : undefined, - requestData: Array.isArray(request.requestData) - ? Base64.toUint8Array(request.requestData[0]) - : request.requestData, - responseData: Array.isArray(request.responseData) - ? Base64.toUint8Array(request.responseData[0]) - : request.responseData, }); }); }); return { + db, columns, routes, nextRouteId, @@ -448,11 +468,12 @@ export function plugin(client: PluginClient<Events, Methods>) { <> <Menu.Item key="curl" - onClick={() => { + onClick={async () => { if (!request) { return; } - const command = convertRequestToCurlCommand(request); + const requestWithData = await db.addDataToRequest(request); + const command = convertRequestToCurlCommand(requestWithData); client.writeTextToClipboard(command); }}> Copy cURL command @@ -541,8 +562,8 @@ function createRequestFromRequestInfo( method: data.method, url: data.url ?? '', domain, + requestLength: getRequestLength(data.headers, data.data), requestHeaders: data.headers, - requestData: decodeBody(data.headers, data.data), status: '...', }; customColumns @@ -570,7 +591,6 @@ function updateRequestWithResponseInfo( responseData: decodeBody(response.headers, response.data), responseIsMock: response.isMock, responseLength: getResponseLength(response), - requestLength: getRequestLength(request), duration: response.timestamp - request.requestTime.getTime(), insights: response.insights ?? undefined, }; @@ -641,14 +661,32 @@ export function Component() { ); } +const NOT_FETCHED = Symbol('not fetched'); + function Sidebar() { const instance = usePlugin(plugin); const selectedId = useValue(instance.selectedId); const detailBodyFormat = useValue(instance.detailBodyFormat); + const [requestWithData, setRequestWithData] = useState< + RequestWithData | typeof NOT_FETCHED + >(NOT_FETCHED); + const db = instance.db; // TODO: Fix this the next time the file is edited. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const request = instance.requests.getById(selectedId!); + + useEffect(() => { + async function fetchDataFromDB() { + if (!request) { + return; + } + const requestWithData = await db.addDataToRequest(request); + setRequestWithData(requestWithData); + } + fetchDataFromDB(); + }, [db, request, setRequestWithData]); + if (!request) { return ( <Layout.Container pad grow center> @@ -657,10 +695,12 @@ function Sidebar() { ); } - return ( + return requestWithData === NOT_FETCHED ? ( + <Spin /> + ) : ( <RequestDetails key={selectedId} - request={request} + request={requestWithData} bodyFormat={detailBodyFormat} onSelectFormat={instance.onSelectFormat} onCopyText={instance.onCopyText} @@ -682,14 +722,6 @@ const baseColumns: DataTableColumn<Request>[] = [ visible: false, powerSearchConfig: {type: 'dateTime'}, }, - { - key: 'requestData', - title: 'GraphQL operation name', - width: 120, - visible: false, - formatters: formatOperationName, - powerSearchConfig: {type: 'object'}, - }, { key: 'domain', powerSearchConfig: {type: 'string'}, diff --git a/desktop/plugins/public/network/package.json b/desktop/plugins/public/network/package.json index 2815aca00b8..ea5bc731d12 100644 --- a/desktop/plugins/public/network/package.json +++ b/desktop/plugins/public/network/package.json @@ -22,7 +22,8 @@ "pako": "^2.0.3", "protobufjs": "^6.10.2", "xml-beautifier": "^0.4.0", - "lossless-json": "^4.0.1" + "lossless-json": "^4.0.1", + "idb": "^8.0.0" }, "peerDependencies": { "flipper": "*", @@ -31,6 +32,9 @@ "devDependencies": { "@types/brotli": "^1.3.1", "@types/pako": "^2.0.0", - "js-base64": "^3.6.0" + "@types/core-js": "^2.5.8", + "js-base64": "^3.6.0", + "fake-indexeddb": "^6.0.0", + "core-js": "^3.37.1" } } diff --git a/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx b/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx index 9322d0bc840..2980f36c47b 100644 --- a/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx +++ b/desktop/plugins/public/network/request-mocking/NetworkRouteManager.tsx @@ -10,6 +10,7 @@ import {Atom, DataTableManager, getFlipperLib} from 'flipper-plugin'; import {createContext} from 'react'; import {Header, Request} from '../types'; +import {RequestDataDB} from '../RequestDataDB'; export type Route = { requestUrl: string; @@ -34,7 +35,7 @@ export interface NetworkRouteManager { modifyRoute(id: string, routeChange: Partial<Route>): void; removeRoute(id: string): void; enableRoute(id: string): void; - copySelectedCalls(): void; + copySelectedCalls(): Promise<void>; importRoutes(): void; exportRoutes(): void; clearRoutes(): void; @@ -47,7 +48,7 @@ export const nullNetworkRouteManager: NetworkRouteManager = { modifyRoute(_id: string, _routeChange: Partial<Route>) {}, removeRoute(_id: string) {}, enableRoute(_id: string) {}, - copySelectedCalls() {}, + async copySelectedCalls() {}, importRoutes() {}, exportRoutes() {}, clearRoutes() {}, @@ -62,6 +63,7 @@ export function createNetworkManager( routes: Atom<{[id: string]: any}>, informClientMockChange: (routes: {[id: string]: any}) => Promise<void>, tableManagerRef: React.RefObject<DataTableManager<Request> | undefined>, + requestDB: RequestDataDB, ): NetworkRouteManager { return { addRoute(): string | undefined { @@ -104,18 +106,18 @@ export function createNetworkManager( } informClientMockChange(routes.get()); }, - copySelectedCalls() { - tableManagerRef.current?.getSelectedItems().forEach((request) => { - // convert headers + + async copySelectedCalls() { + const selectedItems = tableManagerRef.current?.getSelectedItems() || []; + const promises = selectedItems.map(async (request) => { + // Convert headers const headers: {[id: string]: Header} = {}; request.responseHeaders?.forEach((e) => { headers[e.key] = e; }); // no need to convert data, already converted when real call was created - const responseData = - request && request.responseData ? request.responseData : ''; - + const responseData = await requestDB.getResponseData(request.id); const newNextRouteId = nextRouteId.get(); routes.update((draft) => { draft[newNextRouteId.toString()] = { @@ -129,7 +131,7 @@ export function createNetworkManager( }); nextRouteId.set(newNextRouteId + 1); }); - + await Promise.all(promises); informClientMockChange(routes.get()); }, importRoutes() { diff --git a/desktop/plugins/public/network/types.tsx b/desktop/plugins/public/network/types.tsx index 1eef07226de..d1f4166be9c 100644 --- a/desktop/plugins/public/network/types.tsx +++ b/desktop/plugins/public/network/types.tsx @@ -20,13 +20,12 @@ export interface Request { url: string; domain: string; requestHeaders: Array<Header>; - requestData: string | Uint8Array | undefined; + // response responseTime?: Date; status: string; reason?: string; responseHeaders?: Array<Header>; - responseData?: string | Uint8Array | undefined; responseLength?: number; requestLength?: number; responseIsMock?: boolean; @@ -34,6 +33,11 @@ export interface Request { insights?: Insights; } +export interface RequestWithData extends Request { + requestData: string | Uint8Array | undefined; + responseData?: string | Uint8Array | undefined; +} + export type Requests = DataSource<Request, never>; export type SerializedRequest = Omit< diff --git a/desktop/plugins/public/network/utils.tsx b/desktop/plugins/public/network/utils.tsx index 42c80a02536..00240c67dac 100644 --- a/desktop/plugins/public/network/utils.tsx +++ b/desktop/plugins/public/network/utils.tsx @@ -10,7 +10,7 @@ import {Buffer} from 'buffer'; import decompress from 'brotli/decompress'; import pako from 'pako'; -import {Request, Header, ResponseInfo} from './types'; +import {Header, ResponseInfo, RequestWithData} from './types'; import {Base64} from 'js-base64'; import {isInteger, parse} from 'lossless-json'; @@ -157,7 +157,10 @@ export function decodeBody( } export function convertRequestToCurlCommand( - request: Pick<Request, 'method' | 'url' | 'requestHeaders' | 'requestData'>, + request: Pick< + RequestWithData, + 'method' | 'url' | 'requestHeaders' | 'requestData' + >, ): string { let command: string = `curl -v -X ${request.method}`; command += ` ${escapedString(request.url)}`; @@ -235,14 +238,17 @@ export function getResponseLength(response: ResponseInfo): number { return 0; } -export function getRequestLength(request: Request): number { - const lengthString = request.requestHeaders - ? getHeaderValue(request.requestHeaders, 'content-length') +export function getRequestLength( + headers: Array<Header>, + data: string | null | undefined, +): number { + const lengthString = headers + ? getHeaderValue(headers, 'content-length') : undefined; if (lengthString) { return parseInt(lengthString, 10); - } else if (request.requestData) { - return Buffer.byteLength(request.requestData, 'base64'); + } else if (data) { + return Buffer.byteLength(data, 'base64'); } return 0; } @@ -282,7 +288,7 @@ export function formatOperationName(requestData: string): string { } } -export function requestsToText(requests: Request[]): string { +export function requestsToText(requests: RequestWithData[]): string { const request = requests[0]; if (!request || !request.url) { return '<empty request>'; diff --git a/desktop/plugins/public/yarn.lock b/desktop/plugins/public/yarn.lock index 82c51ba8878..a35624a055a 100644 --- a/desktop/plugins/public/yarn.lock +++ b/desktop/plugins/public/yarn.lock @@ -251,6 +251,11 @@ dependencies: "@types/node" "*" +"@types/core-js@^2.5.8": + version "2.5.8" + resolved "https://registry.yarnpkg.com/@types/core-js/-/core-js-2.5.8.tgz#d5c6ec44f2f3328653dce385ae586bd8261f8e85" + integrity sha512-VgnAj6tIAhJhZdJ8/IpxdatM8G4OD3VWGlp6xIxUGENZlpbob9Ty4VVdC1FIEp0aK6DBscDDjyzy5FB60TuNqg== + "@types/d3-path@^1": version "1.0.9" resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.9.tgz#73526b150d14cd96e701597cbf346cfd1fd4a58c" @@ -686,6 +691,11 @@ core-js-pure@^3.0.0: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813" integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA== +core-js@^3.37.1: + version "3.37.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.37.1.tgz#d21751ddb756518ac5a00e4d66499df981a62db9" + integrity sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw== + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -1028,6 +1038,11 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" +fake-indexeddb@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/fake-indexeddb/-/fake-indexeddb-6.0.0.tgz#3173d5ad141436dace95f8de6e9ecdc3d9787d5d" + integrity sha512-YEboHE5VfopUclOck7LncgIqskAqnv4q0EWbYCaxKKjAvO93c+TJIaBuGy8CBFdbg9nKdpN3AuPRwVBJ4k7NrQ== + fast-equals@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-2.0.0.tgz#bef2c423af3939f2c54310df54c57e64cd2adefc" @@ -1246,6 +1261,11 @@ hotkeys-js@3.9.4: resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.9.4.tgz#ce1aa4c3a132b6a63a9dd5644fc92b8a9b9cbfb9" integrity sha512-2zuLt85Ta+gIyvs4N88pCYskNrxf1TFv3LR9t5mdAZIX8BcgQQ48F2opUptvHa6m8zsy5v/a0i9mWzTrlNWU0Q== +idb@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/idb/-/idb-8.0.0.tgz#33d7ed894ed36e23bcb542fb701ad579bfaad41f" + integrity sha512-l//qvlAKGmQO31Qn7xdzagVPPaHTxXx199MhrAFuVBTPqydcPYBWjkrbv4Y0ktB+GmWOiwHl237UUOrLmQxLvw== + ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" diff --git a/desktop/scripts/verify-types-dependencies.tsx b/desktop/scripts/verify-types-dependencies.tsx index d68347d0ce0..e16035a4df8 100644 --- a/desktop/scripts/verify-types-dependencies.tsx +++ b/desktop/scripts/verify-types-dependencies.tsx @@ -33,6 +33,7 @@ const IGNORED_TYPES = new Set( 'inquirer', 'mock-fs', 'npm-packlist', + 'core-js', ].map((x) => `@types/${x}`), ); From a25e14bb10299cbb3b49d3b6c3d65ba24487c5b9 Mon Sep 17 00:00:00 2001 From: Luke De Feo <lukedefeo@meta.com> Date: Mon, 3 Jun 2024 08:43:38 -0700 Subject: [PATCH 54/92] Network errors show up as errors Summary: We were only showing http errors as errors but if there was some kind of networking error this should be shown as an error also Reviewed By: antonk52 Differential Revision: D57865639 fbshipit-source-id: 72487338bfe43d7ebe35685151c48af9deca6935 --- desktop/plugins/public/network/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/desktop/plugins/public/network/index.tsx b/desktop/plugins/public/network/index.tsx index e46f4955c0c..6d40113ab3e 100644 --- a/desktop/plugins/public/network/index.tsx +++ b/desktop/plugins/public/network/index.tsx @@ -790,8 +790,8 @@ function getRowStyle(row: Request) { ? mockingStyle : row.status && row.status !== '...' && - parseInt(row.status, 10) >= 400 && - parseInt(row.status, 10) < 600 + ((parseInt(row.status, 10) >= 400 && parseInt(row.status, 10) < 600) || + parseInt(row.status, 10) === -1) ? errorStyle : undefined; } From d7a2c3f1dc06bc93c97997475dff395b30131645 Mon Sep 17 00:00:00 2001 From: Addison Howenstine <addisonh@meta.com> Date: Tue, 4 Jun 2024 16:57:44 -0700 Subject: [PATCH 55/92] Create Autofill plugin Summary: This diff adds a new Flipper plugin for the Autofill codebase which allows us to fetch the new AutofillService data model and display it in the GUI in a collapsable, comparable way. Subsequent diffs will hook this into both our android and ios apps. https://www.internalfb.com/intern/staticdocs/flipper/docs/tutorial/intro/ Reviewed By: dejesus2010 Differential Revision: D57597664 fbshipit-source-id: 693e2db88fdfe3766c782b45a3bd7afc4c9df387 --- desktop/flipper-plugin/package.json | 10 +- desktop/yarn.lock | 137 ++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 4 deletions(-) diff --git a/desktop/flipper-plugin/package.json b/desktop/flipper-plugin/package.json index c587b9d442c..eba5c24a86d 100644 --- a/desktop/flipper-plugin/package.json +++ b/desktop/flipper-plugin/package.json @@ -17,21 +17,23 @@ "@types/react-color": "2.13.5", "@types/react-dom": "^17.0.13", "dayjs": "^1.11.10", + "eventemitter3": "^4.0.7", "flipper-common": "0.0.0", "immer": "^9.0.18", "js-base64": "^3.7.5", "lodash": "^4.17.21", "react-color": "^2.19.3", "react-element-to-jsx-string": "^14.3.4", + "react-json-view": "^1.21.3", + "react-json-view-compare": "^2.0.2", "react-use": "^17.4.0", "react-virtual": "^2.10.4", - "string-natural-compare": "^3.0.0", - "eventemitter3": "^4.0.7" + "string-natural-compare": "^3.0.0" }, "devDependencies": { + "@types/react": "17.0.39", "@types/string-natural-compare": "^3.0.2", - "jest-mock-console": "^1.2.3", - "@types/react": "17.0.39" + "jest-mock-console": "^1.2.3" }, "peerDependencies": { "@ant-design/icons": "^4.2.2", diff --git a/desktop/yarn.lock b/desktop/yarn.lock index 62f618e0a9a..87e14902f0f 100644 --- a/desktop/yarn.lock +++ b/desktop/yarn.lock @@ -1948,6 +1948,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.20.13": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.6.tgz#5b76eb89ad45e2e4a0a8db54c456251469a3358e" + integrity sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.23.2": version "7.23.8" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.8.tgz#8ee6fe1ac47add7122902f257b8ddf55c898f650" @@ -4634,6 +4641,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base16@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70" + integrity sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ== + base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -5361,6 +5373,13 @@ cross-fetch@^3.0.4: node-fetch "2.6.0" whatwg-fetch "3.0.0" +cross-fetch@^3.1.5: + version "3.1.8" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.8.tgz#0327eba65fd68a7d119f8fb2bf9334a1a7956f82" + integrity sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg== + dependencies: + node-fetch "^2.6.12" + cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -6719,6 +6738,13 @@ fb-watchman@^2.0.2: dependencies: bser "2.1.1" +fbemitter@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/fbemitter/-/fbemitter-3.0.0.tgz#00b2a1af5411254aab416cd75f9e6289bee4bff3" + integrity sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw== + dependencies: + fbjs "^3.0.0" + fbjs-css-vars@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8" @@ -6737,6 +6763,19 @@ fbjs@^3.0.0: setimmediate "^1.0.5" ua-parser-js "^0.7.18" +fbjs@^3.0.1: + version "3.0.5" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.5.tgz#aa0edb7d5caa6340011790bd9249dbef8a81128d" + integrity sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg== + dependencies: + cross-fetch "^3.1.5" + fbjs-css-vars "^1.0.0" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^1.0.35" + fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -6853,6 +6892,14 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== +flux@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/flux/-/flux-4.0.4.tgz#9661182ea81d161ee1a6a6af10d20485ef2ac572" + integrity sha512-NCj3XlayA2UsapRpM7va6wU1+9rE5FIL7qoMcmxWHRzbp0yujihMBm9BBHZ1MDIk5h5o2Bl6eGiCe8rYELAmYw== + dependencies: + fbemitter "^3.0.0" + fbjs "^3.0.1" + follow-redirects@1.5.10: version "1.5.10" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" @@ -8897,6 +8944,11 @@ lodash._reinterpolate@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" integrity sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA== +lodash.curry@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170" + integrity sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA== + lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -8917,6 +8969,11 @@ lodash.flatten@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= +lodash.flow@^3.3.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/lodash.flow/-/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a" + integrity sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw== + lodash.includes@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" @@ -9599,6 +9656,13 @@ node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.6, node dependencies: whatwg-url "^5.0.0" +node-fetch@^2.6.12: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + node-forge@^0.10.0, node-forge@^0.7.1, node-forge@^1.0.6: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" @@ -10288,6 +10352,11 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +pure-color@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e" + integrity sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA== + pure-rand@^6.0.0: version "6.0.2" resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.2.tgz#a9c2ddcae9b68d736a8163036f088a2781c8b306" @@ -10782,6 +10851,16 @@ react-async@^10.0.0: resolved "https://registry.yarnpkg.com/react-async/-/react-async-10.0.1.tgz#575c083f808303d2f6ca52d11ec7554dbdbd9fcd" integrity sha512-ORUz5ca0B57QgBIzEZM5SuhJ6xFjkvEEs0gylLNlWf06vuVcLZsjIw3wx58jJkZG38p+0nUAxRgFW2b7mnVZzA== +react-base16-styling@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/react-base16-styling/-/react-base16-styling-0.6.0.tgz#ef2156d66cf4139695c8a167886cb69ea660792c" + integrity sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ== + dependencies: + base16 "^1.0.0" + lodash.curry "^4.0.1" + lodash.flow "^3.3.0" + pure-color "^1.2.0" + react-color@^2.19.3: version "2.19.3" resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.19.3.tgz#ec6c6b4568312a3c6a18420ab0472e146aa5683d" @@ -10854,6 +10933,33 @@ react-is@^18.0.0, react-is@^18.2.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== +react-json-view-compare@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/react-json-view-compare/-/react-json-view-compare-2.0.2.tgz#7a947caae1a0e133b22e0e327e1ad44cf3929dec" + integrity sha512-we+OMLFR2FGqHeoqfubQnMhY5V4jLK15/cOnh7cj+v6v7XpizdJu4oSfbEwRWxvTzywSTqpBw/xL0kPZ2YkLIg== + dependencies: + react-json-viewer-cool "^2.0.0" + +react-json-view@^1.21.3: + version "1.21.3" + resolved "https://registry.yarnpkg.com/react-json-view/-/react-json-view-1.21.3.tgz#f184209ee8f1bf374fb0c41b0813cff54549c475" + integrity sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw== + dependencies: + flux "^4.0.1" + react-base16-styling "^0.6.0" + react-lifecycles-compat "^3.0.4" + react-textarea-autosize "^8.3.2" + +react-json-viewer-cool@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/react-json-viewer-cool/-/react-json-viewer-cool-2.0.0.tgz#dbce44305899b083f76120eaa14e9f9c96a140b8" + integrity sha512-2bCC9szMbh9PEK5WmAv2253MwjWdK/58RCleqIqvtVjoFFCtWRPXKbe+uK6cxxGRrtCPFLJE+4H1+T+pUQltLQ== + +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + react-markdown@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-6.0.3.tgz#625ec767fa321d91801129387e7d31ee0cb99254" @@ -10923,6 +11029,15 @@ react-test-renderer@^0.0.0-experimental-2bf7c02f0-20220314: react-shallow-renderer "^16.13.1" scheduler "0.0.0-experimental-2bf7c02f0-20220314" +react-textarea-autosize@^8.3.2: + version "8.5.3" + resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz#d1e9fe760178413891484847d3378706052dd409" + integrity sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ== + dependencies: + "@babel/runtime" "^7.20.13" + use-composed-ref "^1.3.0" + use-latest "^1.2.1" + react-universal-interface@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b" @@ -12546,6 +12661,11 @@ ua-parser-js@^0.7.18: resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c" integrity sha512-yo+miGzQx5gakzVK3QFfN0/L9uVhosXBBO7qmnk7c2iw1IhL212wfA3zbnI54B0obGwC/5NWub/iT9sReMx+Fw== +ua-parser-js@^1.0.35: + version "1.0.37" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.37.tgz#b5dc7b163a5c1f0c510b08446aed4da92c46373f" + integrity sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ== + uglify-es@^3.1.9: version "3.3.9" resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" @@ -12737,6 +12857,23 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" +use-composed-ref@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.3.0.tgz#3d8104db34b7b264030a9d916c5e94fbe280dbda" + integrity sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ== + +use-isomorphic-layout-effect@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb" + integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA== + +use-latest@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/use-latest/-/use-latest-1.2.1.tgz#d13dfb4b08c28e3e33991546a2cee53e14038cf2" + integrity sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw== + dependencies: + use-isomorphic-layout-effect "^1.1.1" + util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" From e8eafa79fcd4fb92906edf143707ed52b6d61957 Mon Sep 17 00:00:00 2001 From: Abdul Babil <abdulbabil@meta.com> Date: Wed, 5 Jun 2024 06:51:51 -0700 Subject: [PATCH 56/92] Allow SonarFlowReporter to use an existing Flipper Network Plugin if any was presented, or fall to DI Summary: After adding Graphservice to AMA Android, I tried to integrate Flipper network plugin to capture GS requests and send them to Flipper. However, I noticed that AMA uses Catalyst shell package which will [init Flipper initially](https://www.internalfb.com/code/fbsource/[56757675f594]/fbandroid/java/com/facebook/catalyst/shell/FbReactApplicationBaseSonarUtil.java?lines=25-39) so any attempt to use DI to create a new network plugin to catch GS traffic won't work since Flipper singleton will relay on the first network plugin that was created before DI. This changes will resolve the problem, before letting DI create a new network plugin object, it will look for an existing one so it reuse it, if non were declared then it will create a new instance. Reviewed By: WilliamY97 Differential Revision: D57799647 fbshipit-source-id: 6d059bcc63060f832d2c0bceeeff5f216db6a7a2 --- .../flipper/plugins/network/NetworkFlipperPlugin.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/android/plugins/network/src/main/java/com/facebook/flipper/plugins/network/NetworkFlipperPlugin.java b/android/plugins/network/src/main/java/com/facebook/flipper/plugins/network/NetworkFlipperPlugin.java index 242c45fd923..31f4c3863ff 100644 --- a/android/plugins/network/src/main/java/com/facebook/flipper/plugins/network/NetworkFlipperPlugin.java +++ b/android/plugins/network/src/main/java/com/facebook/flipper/plugins/network/NetworkFlipperPlugin.java @@ -19,8 +19,7 @@ public class NetworkFlipperPlugin extends BufferingFlipperPlugin implements NetworkReporter { public static final String ID = "Network"; private static final int MAX_BODY_SIZE_IN_BYTES = 1024 * 1024; - - private final List<NetworkResponseFormatter> mFormatters; + private List<NetworkResponseFormatter> mFormatters; public NetworkFlipperPlugin() { this(null); @@ -35,6 +34,10 @@ public String getId() { return ID; } + public void setFormatters(List<NetworkResponseFormatter> formatters) { + mFormatters = formatters; + } + @Override public void reportRequest(final RequestInfo requestInfo) { (new ErrorReportingRunnable(getConnection()) { From 8deb99134de07c3a524b6f68678a417a0be030dd Mon Sep 17 00:00:00 2001 From: Bolun Qi <bolunqi@meta.com> Date: Wed, 5 Jun 2024 16:36:29 -0700 Subject: [PATCH 57/92] Make http mocks show up Summary: per title Differential Revision: D58211590 fbshipit-source-id: 99e8ea998aa2b3cdcd6254d384366be4c4dbe252 --- .vscode/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 21592c1639b..480ba89afdc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,7 +17,7 @@ } ], "[typescriptreact]": { - "editor.defaultFormatter": "dbaeumer.vscode-eslint" + "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[typescript]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" From f27d31c7732ac85a7c217ccdb34e91cda7a29722 Mon Sep 17 00:00:00 2001 From: Luke De Feo <lukedefeo@meta.com> Date: Fri, 7 Jun 2024 03:04:38 -0700 Subject: [PATCH 58/92] Add abilty to restart flipper server from trouble shoot menu Summary: Hopefully this will be easier to descibe and find for users than the small icon in mac os menu bar Reviewed By: passy Differential Revision: D58282148 fbshipit-source-id: 87f3474c317f38ce906bd3d6ab09b2a2da1557cd --- desktop/flipper-ui/src/sandy-chrome/Navbar.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/desktop/flipper-ui/src/sandy-chrome/Navbar.tsx b/desktop/flipper-ui/src/sandy-chrome/Navbar.tsx index 48d8c2516a1..52326841af5 100644 --- a/desktop/flipper-ui/src/sandy-chrome/Navbar.tsx +++ b/desktop/flipper-ui/src/sandy-chrome/Navbar.tsx @@ -557,6 +557,13 @@ function TroubleshootMenu() { }}> Restart ADB (Android connections) </Menu.Item> + <Menu.Item + key="restart-flipper-serer" + onClick={() => { + getFlipperServer().exec('restart'); + }}> + Restart Flipper Server + </Menu.Item> </Menu.SubMenu> </Menu> </Badge> From 6a610b9a7a378ea7d1a26dde7bf586706b0011f8 Mon Sep 17 00:00:00 2001 From: Pascal Hartig <realpassy@meta.com> Date: Mon, 10 Jun 2024 11:22:09 -0700 Subject: [PATCH 59/92] Edit deeplinks.mdx using inpage editor Summary: This diff has been automatically generated by the inpage editor. NOTE: If you want to update this diff, go via the preview link inside the static docs section below. Ensure you are editing the same page that was used to create this diff. Reviewed By: antonk52 Differential Revision: D58364842 fbshipit-source-id: 10845ea447f93f5727b51eea16912f9bdbcb843e --- docs/extending/deeplinks.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/extending/deeplinks.mdx b/docs/extending/deeplinks.mdx index b55aa1ac218..560e0556f68 100644 --- a/docs/extending/deeplinks.mdx +++ b/docs/extending/deeplinks.mdx @@ -3,7 +3,7 @@ id: deeplinks title: Using Deeplinks --- -Flipper supports opening Flipper through deeplinks via the `flipper://` protocol. +Flipper supports opening Flipper through deeplinks via the `web+flipper://` protocol. ## open-plugin From 2c0b48994368a068f38b68aa1ccd02b021e256e0 Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa <lblasa@meta.com> Date: Tue, 11 Jun 2024 08:59:31 -0700 Subject: [PATCH 60/92] Fix multiple fake devices during QR exchange Summary: Device UDID is not available during the exchange. As such, we do a best attempt to find devices by name. This is fine except we append "via QR exchange" to said name which then makes it impossible to match. Displaying "via QR exchange" also exposes an implementation detail which does not have a direct relationship with the device. so removing this from the dropdown displayed title seems appropriate. Reviewed By: antonk52 Differential Revision: D58415038 fbshipit-source-id: 1518627d58c0134b0ed2c5e981c85ca2a492b992 --- .../flipper-server/src/app-connectivity/ServerController.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/flipper-server/src/app-connectivity/ServerController.tsx b/desktop/flipper-server/src/app-connectivity/ServerController.tsx index 1208a922fed..f917828d5d4 100644 --- a/desktop/flipper-server/src/app-connectivity/ServerController.tsx +++ b/desktop/flipper-server/src/app-connectivity/ServerController.tsx @@ -442,7 +442,7 @@ export class ServerController new DummyDevice( this.flipperServer, deviceId, - `${clientQuery.device} via QR Exchange`, + clientQuery.device, clientQuery.os, ), ); From 1426751890d28f29306fc464d613f0313e0f674c Mon Sep 17 00:00:00 2001 From: generatedunixname89002005306973 <generatedunixname89002005306973@meta.com> Date: Wed, 12 Jun 2024 06:33:03 -0700 Subject: [PATCH 61/92] Flipper Release: v0.255.0 Summary: Releasing version 0.255.0 Reviewed By: antonk52 Differential Revision: D58462272 fbshipit-source-id: b846dd43e247de4809b0f1d7e29947ba43ae8c81 --- desktop/package.json | 2 +- desktop/plugins/public/layout/docs/setup.mdx | 2 +- desktop/plugins/public/leak_canary/docs/setup.mdx | 2 +- desktop/plugins/public/network/docs/setup.mdx | 2 +- desktop/static/CHANGELOG.md | 5 +++++ docs/getting-started/android-native.mdx | 4 ++-- docs/getting-started/react-native-ios.mdx | 2 +- docs/getting-started/react-native.mdx | 4 ++-- gradle.properties | 2 +- js/js-flipper/package.json | 2 +- react-native/react-native-flipper/package.json | 2 +- 11 files changed, 17 insertions(+), 12 deletions(-) diff --git a/desktop/package.json b/desktop/package.json index 3a9387c4bd5..32c06d80a0e 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -164,7 +164,7 @@ "npm": "use yarn instead", "yarn": "^1.16" }, - "version": "0.254.0", + "version": "0.255.0", "workspaces": { "packages": [ "scripts", diff --git a/desktop/plugins/public/layout/docs/setup.mdx b/desktop/plugins/public/layout/docs/setup.mdx index 95c7991e77b..55faad0f3d6 100644 --- a/desktop/plugins/public/layout/docs/setup.mdx +++ b/desktop/plugins/public/layout/docs/setup.mdx @@ -27,7 +27,7 @@ You also need to compile in the `litho-annotations` package, as Flipper reflects ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.254.0' + debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.255.0' debugImplementation 'com.facebook.litho:litho-annotations:0.19.0' // ... } diff --git a/desktop/plugins/public/leak_canary/docs/setup.mdx b/desktop/plugins/public/leak_canary/docs/setup.mdx index 6ec4c30eda4..b4a233d6c5c 100644 --- a/desktop/plugins/public/leak_canary/docs/setup.mdx +++ b/desktop/plugins/public/leak_canary/docs/setup.mdx @@ -8,7 +8,7 @@ To setup the <Link to={useBaseUrl("/docs/features/plugins/leak-canary")}>LeakCan ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.254.0' + debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.255.0' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1' } ``` diff --git a/desktop/plugins/public/network/docs/setup.mdx b/desktop/plugins/public/network/docs/setup.mdx index a4f3c7dbc4f..86f2f08ab05 100644 --- a/desktop/plugins/public/network/docs/setup.mdx +++ b/desktop/plugins/public/network/docs/setup.mdx @@ -11,7 +11,7 @@ The network plugin is shipped as a separate Maven artifact, as follows: ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.254.0' + debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.255.0' } ``` diff --git a/desktop/static/CHANGELOG.md b/desktop/static/CHANGELOG.md index 411dafb2c49..e71ad4a4995 100644 --- a/desktop/static/CHANGELOG.md +++ b/desktop/static/CHANGELOG.md @@ -1,3 +1,8 @@ +# 0.255.0 (12/6/2024) + + * [D57865621](https://github.com/facebook/flipper/search?q=D57865621&type=Commits) - Network plugin: Store data in IndexedDB to reduce memory consumption + + # 0.253.0 (15/5/2024) * [D55967421](https://github.com/facebook/flipper/search?q=D55967421&type=Commits) - BloksDebugger - Async component payload support (android only currently) diff --git a/docs/getting-started/android-native.mdx b/docs/getting-started/android-native.mdx index 8961f18e0ea..209930d368e 100644 --- a/docs/getting-started/android-native.mdx +++ b/docs/getting-started/android-native.mdx @@ -24,10 +24,10 @@ repositories { } dependencies { - debugImplementation 'com.facebook.flipper:flipper:0.254.0' + debugImplementation 'com.facebook.flipper:flipper:0.255.0' debugImplementation 'com.facebook.soloader:soloader:0.10.5' - releaseImplementation 'com.facebook.flipper:flipper-noop:0.254.0' + releaseImplementation 'com.facebook.flipper:flipper-noop:0.255.0' } ``` diff --git a/docs/getting-started/react-native-ios.mdx b/docs/getting-started/react-native-ios.mdx index 273037d14c7..2d0deb47428 100644 --- a/docs/getting-started/react-native-ios.mdx +++ b/docs/getting-started/react-native-ios.mdx @@ -51,7 +51,7 @@ Add all of the code below to your `ios/Podfile`: platform :ios, '9.0' def flipper_pods() - flipperkit_version = '0.254.0' # should match the version of your Flipper client app + flipperkit_version = '0.255.0' # should match the version of your Flipper client app pod 'FlipperKit', '~>' + flipperkit_version, :configuration => 'Debug' pod 'FlipperKit/FlipperKitLayoutPlugin', '~>' + flipperkit_version, :configuration => 'Debug' pod 'FlipperKit/SKIOSNetworkPlugin', '~>' + flipperkit_version, :configuration => 'Debug' diff --git a/docs/getting-started/react-native.mdx b/docs/getting-started/react-native.mdx index ca3f349f141..6f19400b85e 100644 --- a/docs/getting-started/react-native.mdx +++ b/docs/getting-started/react-native.mdx @@ -34,7 +34,7 @@ Latest version of Flipper requires react-native 0.69+! If you use react-native < Android: -1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.254.0`. +1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.255.0`. 2. Run `./gradlew clean` in the `android` directory. iOS: @@ -44,7 +44,7 @@ react-native version => 0.69.0 2. Run `pod install --repo-update` in the `ios` directory. react-native version < 0.69.0 -1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.254.0' })`. +1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.255.0' })`. 2. Run `pod install --repo-update` in the `ios` directory. ## Manual Setup diff --git a/gradle.properties b/gradle.properties index cb90622de76..5fc20ee5db8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. # POM publishing constants -VERSION_NAME=0.254.1-SNAPSHOT +VERSION_NAME=0.255.0 GROUP=com.facebook.flipper SONATYPE_STAGING_PROFILE=comfacebook POM_URL=https://github.com/facebook/flipper diff --git a/js/js-flipper/package.json b/js/js-flipper/package.json index 61d6e8af8ce..486e2932517 100644 --- a/js/js-flipper/package.json +++ b/js/js-flipper/package.json @@ -1,7 +1,7 @@ { "name": "js-flipper", "title": "JS Flipper Bindings for Web-Socket based clients", - "version": "0.254.0", + "version": "0.255.0", "main": "lib/index.js", "browser": { "os": false diff --git a/react-native/react-native-flipper/package.json b/react-native/react-native-flipper/package.json index 559c03bf3fd..280d967bb0c 100644 --- a/react-native/react-native-flipper/package.json +++ b/react-native/react-native-flipper/package.json @@ -1,7 +1,7 @@ { "name": "react-native-flipper", "title": "React Native Flipper Bindings", - "version": "0.254.0", + "version": "0.255.0", "description": "Flipper bindings for React Native", "main": "index.js", "types": "index.d.ts", From b69b44f85d9b94de8de848d67d0cf2b576cd9208 Mon Sep 17 00:00:00 2001 From: generatedunixname89002005306973 <generatedunixname89002005306973@meta.com> Date: Wed, 12 Jun 2024 06:33:03 -0700 Subject: [PATCH 62/92] Flipper Snapshot Bump: v0.255.1-SNAPSHOT Summary: Releasing snapshot version 0.255.1-SNAPSHOT Reviewed By: antonk52 Differential Revision: D58462273 fbshipit-source-id: ef89b009e3acb79a190f823734e4981f13b09671 --- docs/getting-started/android-native.mdx | 4 ++-- gradle.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/android-native.mdx b/docs/getting-started/android-native.mdx index 209930d368e..82ae9d8ff5c 100644 --- a/docs/getting-started/android-native.mdx +++ b/docs/getting-started/android-native.mdx @@ -124,10 +124,10 @@ repositories { } dependencies { - debugImplementation 'com.facebook.flipper:flipper:0.254.1-SNAPSHOT' + debugImplementation 'com.facebook.flipper:flipper:0.255.1-SNAPSHOT' debugImplementation 'com.facebook.soloader:soloader:0.10.5' - releaseImplementation 'com.facebook.flipper:flipper-noop:0.254.1-SNAPSHOT' + releaseImplementation 'com.facebook.flipper:flipper-noop:0.255.1-SNAPSHOT' } ``` diff --git a/gradle.properties b/gradle.properties index 5fc20ee5db8..fab5bb28860 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. # POM publishing constants -VERSION_NAME=0.255.0 +VERSION_NAME=0.255.1-SNAPSHOT GROUP=com.facebook.flipper SONATYPE_STAGING_PROFILE=comfacebook POM_URL=https://github.com/facebook/flipper From 46a7607e9df5a03876ee4fd106a4fee953d7c9b1 Mon Sep 17 00:00:00 2001 From: Pascal Hartig <realpassy@meta.com> Date: Wed, 12 Jun 2024 07:32:26 -0700 Subject: [PATCH 63/92] Edit deeplinks.mdx using inpage editor Summary: This diff has been automatically generated by the inpage editor. NOTE: If you want to update this diff, go via the preview link inside the static docs section below. Ensure you are editing the same page that was used to create this diff. Reviewed By: elboman Differential Revision: D58459074 fbshipit-source-id: 8c645b81ae65988fc97cbd2d774f6e7962616794 --- docs/extending/deeplinks.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/extending/deeplinks.mdx b/docs/extending/deeplinks.mdx index 560e0556f68..65f45a51ef7 100644 --- a/docs/extending/deeplinks.mdx +++ b/docs/extending/deeplinks.mdx @@ -9,7 +9,7 @@ Flipper supports opening Flipper through deeplinks via the `web+flipper://` prot The following link format can be used to open Flipper and open a specific plugin: -`flipper://open-plugin?plugin-id=<plugin-id>&client=<client>&devices=<devices>&payload=<payload>` +`web+flipper://open-plugin?plugin-id=<plugin-id>&client=<client>&devices=<devices>&payload=<payload>` The parameters are specified as follows: From ff71c3b242dd1493436d816816404d2ec170d103 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy <antonk52@meta.com> Date: Thu, 13 Jun 2024 02:00:53 -0700 Subject: [PATCH 64/92] remove unneeded log during flipper build Summary: This was committed accidentally during debugging. Build output is verbose enough already. Reviewed By: mweststrate Differential Revision: D58466513 fbshipit-source-id: 86838f47861cc3eda83402ae21bae7bd0ff36af3 --- desktop/scripts/build-icons.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/desktop/scripts/build-icons.tsx b/desktop/scripts/build-icons.tsx index ee6b91771c0..cf441240875 100644 --- a/desktop/scripts/build-icons.tsx +++ b/desktop/scripts/build-icons.tsx @@ -65,7 +65,6 @@ export async function downloadIcons(buildFolder: string) { // not available at this size, pick the next continue; } - console.log(`🔵 icon url: ${url}`); return new Promise((resolve, reject) => { const fileStream = fs.createWriteStream( path.join(buildFolder, buildLocalIconPath(icon)), From 6b44de3fb395db990c851cc6a25521cc23fb0323 Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa <lblasa@meta.com> Date: Thu, 13 Jun 2024 07:54:43 -0700 Subject: [PATCH 65/92] Fix deep-links into Flipper Summary: There was an ongoing issue whereas deep-links were not being opened when Flipper was already running. Also, the existing deep-link infra assumed the action was always going to be "open-plugin" which may not be the case. This change addresses both issues. ## Change - Attempt to open a deep-link from the service worker consumer. This ensures deep-link handling is done both from cold-start and when running. - Update manifest and logic as to incorporate back the "open-plugin" action. Reviewed By: passy Differential Revision: D58527089 fbshipit-source-id: 33248ebe286ac079c8b1c2b381bf0f46e52c3e9c --- desktop/flipper-ui/src/index.tsx | 73 ++++++++++++++++++--------- desktop/static/manifest.template.json | 2 +- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/desktop/flipper-ui/src/index.tsx b/desktop/flipper-ui/src/index.tsx index 8ad401a7b14..0ac9cc7f31e 100644 --- a/desktop/flipper-ui/src/index.tsx +++ b/desktop/flipper-ui/src/index.tsx @@ -45,6 +45,30 @@ let cachedDeepLinkURL: string | undefined; const logger = initLogger(); +const maybeDeepLinkURLFromSearchParams = (searchParams: URLSearchParams) => { + const deepLink = searchParams.get('deep-link'); + if (deepLink) { + function removePrefix(input: string, prefix: string): string { + const regex = new RegExp(`^${prefix}+`); + return input.replace(regex, ''); + } + + const url = new URL(deepLink); + const hostname = removePrefix(url.pathname, '/'); + const params = url.searchParams; + + const deeplinkURL = new URL(`flipper://${hostname.toString()}`); + deeplinkURL.search = params.toString(); + + return deeplinkURL.toString(); + } +}; + +const maybeDeepLinkURLFromURL = (url: string) => { + const searchParams = new URL(url).searchParams; + return maybeDeepLinkURLFromSearchParams(searchParams); +}; + async function start() { /** * The following is used to ensure only one instance of Flipper is running at a time. @@ -114,21 +138,9 @@ async function start() { return token; }; - const openPlugin = params.get('open-plugin'); - if (openPlugin) { - function removePrefix(input: string, prefix: string): string { - const regex = new RegExp(`^${prefix}+`); - return input.replace(regex, ''); - } - - const url = new URL(openPlugin); - const maybeParams = removePrefix(url.pathname, '/'); - const params = new URLSearchParams(maybeParams); - - const deeplinkURL = new URL('flipper://open-plugin'); - deeplinkURL.search = params.toString(); - - cachedDeepLinkURL = deeplinkURL.toString(); + const deepLinkURL = maybeDeepLinkURLFromSearchParams(params); + if (deepLinkURL) { + cachedDeepLinkURL = deepLinkURL.toString(); } getLogger().info('[flipper-client][ui-browser] Create WS client'); @@ -259,20 +271,33 @@ async function initializePWA() { // @ts-ignore window.launchQueue.setConsumer(async (launchParams) => { - if (!launchParams || !launchParams.files) { + if (!launchParams) { return; } - getLogger().debug('[PWA] Attempt to to open a file'); - for (const file of launchParams.files) { - const blob = await file.getFile(); - blob.handle = file; + if (launchParams.files && launchParams.files.length > 0) { + getLogger().debug('[PWA] Attempt to to open a file'); + for (const file of launchParams.files) { + const blob = await file.getFile(); + blob.handle = file; + + const data = await blob.text(); + const name = file.name; - const data = await blob.text(); - const name = file.name; + cachedFile = {name, data}; - cachedFile = {name, data}; + openFileIfAny(); + return; + } + } - openFileIfAny(); + if (launchParams.targetURL) { + getLogger().debug('[PWA] Attempt to to open an URL'); + const deepLinkURL = maybeDeepLinkURLFromURL(launchParams.targetURL); + if (deepLinkURL) { + cachedDeepLinkURL = deepLinkURL.toString(); + } + openURLIfAny(); + return; } }); } else { diff --git a/desktop/static/manifest.template.json b/desktop/static/manifest.template.json index adc08aacf63..9f1dd243b27 100644 --- a/desktop/static/manifest.template.json +++ b/desktop/static/manifest.template.json @@ -31,7 +31,7 @@ "protocol_handlers": [ { "protocol": "web+flipper", - "url": "/?open-plugin=%s" + "url": "/?deep-link=%s" } ] } From d517c0dee99b12d2b3e7db84f1617abfb6f9450c Mon Sep 17 00:00:00 2001 From: Pascal Hartig <realpassy@meta.com> Date: Fri, 14 Jun 2024 04:51:01 -0700 Subject: [PATCH 66/92] Remove debug logging Summary: $title Reviewed By: elboman Differential Revision: D58582848 fbshipit-source-id: 3b999982a7ad6d28aba4e30df2910c8aa2a3114a --- .../src/sandy-chrome/appinspect/PluginMemoryWarning.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginMemoryWarning.tsx b/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginMemoryWarning.tsx index dffab5d5637..f6412f3af72 100644 --- a/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginMemoryWarning.tsx +++ b/desktop/flipper-ui/src/sandy-chrome/appinspect/PluginMemoryWarning.tsx @@ -194,8 +194,6 @@ function PluginMemoryDetails({ } as PluginMemoryStats; }); - console.log('rendeing modal'); - return ( <Layout.Container style={{minHeight: '80vh', width: '100%', display: 'flex'}}> From ca2b99fdc435d3e22b36fe91a4916d0c0b12c38d Mon Sep 17 00:00:00 2001 From: Luke De Feo <lukedefeo@meta.com> Date: Fri, 14 Jun 2024 06:55:56 -0700 Subject: [PATCH 67/92] Only perform watchman health check in development Summary: This healthcheck is only needed for metro / local development so running it in production builds is just noisy for the user Reviewed By: antonk52 Differential Revision: D58580890 fbshipit-source-id: 582844e475a0c8eb72dd493ebd0b2a37c0a16089 --- desktop/doctor/src/cli.tsx | 2 +- desktop/doctor/src/index.tsx | 42 ++++++++++++------- desktop/flipper-common/src/doctor.tsx | 1 + .../src/utils/runHealthchecks.tsx | 4 +- .../flipper-ui/src/utils/runHealthchecks.tsx | 9 +++- 5 files changed, 38 insertions(+), 20 deletions(-) diff --git a/desktop/doctor/src/cli.tsx b/desktop/doctor/src/cli.tsx index 668b36a4fbc..dd1b1089776 100644 --- a/desktop/doctor/src/cli.tsx +++ b/desktop/doctor/src/cli.tsx @@ -13,7 +13,7 @@ import {getEnvInfo} from './environmentInfo'; (async () => { const environmentInfo = await getEnvInfo(); console.log(JSON.stringify(environmentInfo)); - const healthchecks = getHealthchecks(); + const healthchecks = getHealthchecks(false); const results = await Promise.all( Object.entries(healthchecks).map(async ([key, category]) => [ key, diff --git a/desktop/doctor/src/index.tsx b/desktop/doctor/src/index.tsx index 207153ada9b..660581a3dac 100644 --- a/desktop/doctor/src/index.tsx +++ b/desktop/doctor/src/index.tsx @@ -20,7 +20,9 @@ import type {FlipperDoctor} from 'flipper-common'; import * as fs_extra from 'fs-extra'; import {validateSelectedXcodeVersion} from './fb-stubs/validateSelectedXcodeVersion'; -export function getHealthchecks(): FlipperDoctor.Healthchecks { +export function getHealthchecks( + isProduction: boolean, +): FlipperDoctor.Healthchecks { return { common: { label: 'Common', @@ -40,19 +42,24 @@ export function getHealthchecks(): FlipperDoctor.Healthchecks { }; }, }, - { - key: 'common.watchman', - label: 'Watchman Installed', - run: async (_: FlipperDoctor.EnvironmentInfo) => { - const isAvailable = await isWatchmanAvailable(); - return { - hasProblem: !isAvailable, - message: isAvailable - ? ['common.watchman--installed'] - : ['common.watchman--not_installed'], - }; - }, - }, + + ...(!isProduction + ? [ + { + key: 'common.watchman', + label: 'Watchman Installed', + run: async (_: FlipperDoctor.EnvironmentInfo) => { + const isAvailable = await isWatchmanAvailable(); + return { + hasProblem: !isAvailable, + message: isAvailable + ? ['common.watchman--installed'] + : ['common.watchman--not_installed'], + }; + }, + } as FlipperDoctor.Healthcheck, + ] + : []), ], }, android: { @@ -421,11 +428,14 @@ export function getHealthchecks(): FlipperDoctor.Healthchecks { }; } -export async function runHealthchecks(): Promise< +export async function runHealthchecks( + isProduction: boolean, +): Promise< Array<FlipperDoctor.CategoryResult | FlipperDoctor.SkippedHealthcheckCategory> > { const environmentInfo = await getEnvInfo(); - const healthchecks: FlipperDoctor.Healthchecks = getHealthchecks(); + const healthchecks: FlipperDoctor.Healthchecks = + getHealthchecks(isProduction); const results: Array< FlipperDoctor.CategoryResult | FlipperDoctor.SkippedHealthcheckCategory > = await Promise.all( diff --git a/desktop/flipper-common/src/doctor.tsx b/desktop/flipper-common/src/doctor.tsx index 7798b635669..cc444bed1b2 100644 --- a/desktop/flipper-common/src/doctor.tsx +++ b/desktop/flipper-common/src/doctor.tsx @@ -136,6 +136,7 @@ export namespace FlipperDoctor { enablePhysicalIOS: boolean; idbPath: string; }; + isProduction: boolean; }; /** diff --git a/desktop/flipper-server/src/utils/runHealthchecks.tsx b/desktop/flipper-server/src/utils/runHealthchecks.tsx index b9cd80e1929..728ce170680 100644 --- a/desktop/flipper-server/src/utils/runHealthchecks.tsx +++ b/desktop/flipper-server/src/utils/runHealthchecks.tsx @@ -14,7 +14,7 @@ import produce from 'immer'; export async function getHealthChecks( options: FlipperDoctor.HealthcheckSettings, ) { - return produce(getHealthchecks(), (healthchecks) => { + return produce(getHealthchecks(options.isProduction), (healthchecks) => { if (!options.settings.enableAndroid) { healthchecks.android = { label: healthchecks.android.label, @@ -47,7 +47,7 @@ export async function runHealthcheck( categoryName: keyof FlipperDoctor.Healthchecks, ruleName: string, ): Promise<FlipperDoctor.HealthcheckResult> { - const healthchecks = getHealthchecks(); + const healthchecks = getHealthchecks(options.isProduction); const category = healthchecks[categoryName]; if (!category) { throw new Error(`Unknown category: ${categoryName}`); diff --git a/desktop/flipper-ui/src/utils/runHealthchecks.tsx b/desktop/flipper-ui/src/utils/runHealthchecks.tsx index 977562b3a57..8fcb3da14f7 100644 --- a/desktop/flipper-ui/src/utils/runHealthchecks.tsx +++ b/desktop/flipper-ui/src/utils/runHealthchecks.tsx @@ -40,8 +40,12 @@ export type HealthcheckOptions = HealthcheckEventsHandler & HealthcheckSettings; async function launchHealthchecks(options: HealthcheckOptions): Promise<void> { const flipperServer = getFlipperServer(); + + const envInfo = await flipperServer.exec('environment-info'); + const healthchecks = await flipperServer.exec('doctor-get-healthchecks', { settings: options.settings, + isProduction: envInfo.isProduction, }); options.startHealthchecks(healthchecks); let hasProblems = false; @@ -56,7 +60,10 @@ async function launchHealthchecks(options: HealthcheckOptions): Promise<void> { await flipperServer .exec( 'doctor-run-healthcheck', - {settings: options.settings}, + { + settings: options.settings, + isProduction: envInfo.isProduction, + }, categoryKey as keyof FlipperDoctor.Healthchecks, h.key, ) From ea3ba26f1cb19332d629988f3c13832dfadfeda7 Mon Sep 17 00:00:00 2001 From: generatedunixname89002005287564 <generatedunixname89002005287564@meta.com> Date: Tue, 18 Jun 2024 05:58:17 -0700 Subject: [PATCH 68/92] ] Enable for targets in xplat/sonar/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util Reviewed By: pengj Differential Revision: D58720435 fbshipit-source-id: ea89f3bc8013c68102c381a0c40b38fbf5cd72b1 --- .../facebook/internal/androidx/compose/ui/inspection/util/BUCK | 1 + 1 file changed, 1 insertion(+) diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/BUCK b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/BUCK index 82be983656a..4140f21d81c 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/BUCK +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/util/BUCK @@ -3,6 +3,7 @@ load("@fbsource//tools/build_defs/android:fb_android_library.bzl", "fb_android_l fb_android_library( name = "util", abi_generation_mode = "source_only", + k2 = True, oncall = "flipper", deps = [ "//third-party/java/androidx/collection/collection:collection", From ac9091a3b575c62f5c4f79c2a070aa77ee9ed245 Mon Sep 17 00:00:00 2001 From: generatedunixname89002005287564 <generatedunixname89002005287564@meta.com> Date: Tue, 18 Jun 2024 18:12:31 -0700 Subject: [PATCH 69/92] ] Enable for targets in xplat/sonar/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector Differential Revision: D58720045 fbshipit-source-id: c1aded528b336946af7a717a23bfaf21aac5399c --- .../internal/androidx/compose/ui/inspection/inspector/BUCK | 1 + 1 file changed, 1 insertion(+) diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/BUCK b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/BUCK index e7e96c60f3a..aa3d7c3473c 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/BUCK +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/inspector/BUCK @@ -6,6 +6,7 @@ fb_android_library( dataclass_generate = { "mode": "EXPLICIT", }, + k2 = True, oncall = "flipper", deps = [ "//fbandroid/third-party/android/androidx/compose:ui-tooling-data", From 15a9f0009710994099a726ccd803985f067521da Mon Sep 17 00:00:00 2001 From: generatedunixname89002005287564 <generatedunixname89002005287564@meta.com> Date: Tue, 18 Jun 2024 18:14:22 -0700 Subject: [PATCH 70/92] ] Enable for targets in xplat/sonar/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection Differential Revision: D58720794 fbshipit-source-id: 7e3a70d6dc3137fb686cfeff30552c5ee67b60ae --- .../java/facebook/internal/androidx/compose/ui/inspection/BUCK | 1 + 1 file changed, 1 insertion(+) diff --git a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/BUCK b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/BUCK index ae86bb93a74..726ffab3d38 100644 --- a/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/BUCK +++ b/android/plugins/jetpack-compose/src/main/java/facebook/internal/androidx/compose/ui/inspection/BUCK @@ -7,6 +7,7 @@ fb_android_library( dataclass_generate = { "mode": "EXPLICIT", }, + k2 = True, oncall = "flipper", deps = [ "//third-party/java/androidx/compose/ui/ui-android:ui-android-aar", From aa3336e741b9973bef120dcfbf0304dee6a95231 Mon Sep 17 00:00:00 2001 From: generatedunixname89002005306973 <generatedunixname89002005306973@meta.com> Date: Wed, 19 Jun 2024 05:16:16 -0700 Subject: [PATCH 71/92] Flipper Release: v0.256.0 Summary: Releasing version 0.256.0 Reviewed By: mweststrate Differential Revision: D58777924 fbshipit-source-id: 5918afa09d533c7be8cbcb8732e9ff662338f8a2 --- desktop/package.json | 2 +- desktop/plugins/public/layout/docs/setup.mdx | 2 +- desktop/plugins/public/leak_canary/docs/setup.mdx | 2 +- desktop/plugins/public/network/docs/setup.mdx | 2 +- docs/getting-started/android-native.mdx | 4 ++-- docs/getting-started/react-native-ios.mdx | 2 +- docs/getting-started/react-native.mdx | 4 ++-- gradle.properties | 2 +- js/js-flipper/package.json | 2 +- react-native/react-native-flipper/package.json | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/desktop/package.json b/desktop/package.json index 32c06d80a0e..c1bd7c83a9c 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -164,7 +164,7 @@ "npm": "use yarn instead", "yarn": "^1.16" }, - "version": "0.255.0", + "version": "0.256.0", "workspaces": { "packages": [ "scripts", diff --git a/desktop/plugins/public/layout/docs/setup.mdx b/desktop/plugins/public/layout/docs/setup.mdx index 55faad0f3d6..7cde52dfd08 100644 --- a/desktop/plugins/public/layout/docs/setup.mdx +++ b/desktop/plugins/public/layout/docs/setup.mdx @@ -27,7 +27,7 @@ You also need to compile in the `litho-annotations` package, as Flipper reflects ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.255.0' + debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.256.0' debugImplementation 'com.facebook.litho:litho-annotations:0.19.0' // ... } diff --git a/desktop/plugins/public/leak_canary/docs/setup.mdx b/desktop/plugins/public/leak_canary/docs/setup.mdx index b4a233d6c5c..92d198cc07c 100644 --- a/desktop/plugins/public/leak_canary/docs/setup.mdx +++ b/desktop/plugins/public/leak_canary/docs/setup.mdx @@ -8,7 +8,7 @@ To setup the <Link to={useBaseUrl("/docs/features/plugins/leak-canary")}>LeakCan ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.255.0' + debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.256.0' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1' } ``` diff --git a/desktop/plugins/public/network/docs/setup.mdx b/desktop/plugins/public/network/docs/setup.mdx index 86f2f08ab05..0e345c86d99 100644 --- a/desktop/plugins/public/network/docs/setup.mdx +++ b/desktop/plugins/public/network/docs/setup.mdx @@ -11,7 +11,7 @@ The network plugin is shipped as a separate Maven artifact, as follows: ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.255.0' + debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.256.0' } ``` diff --git a/docs/getting-started/android-native.mdx b/docs/getting-started/android-native.mdx index 82ae9d8ff5c..d3f6473e807 100644 --- a/docs/getting-started/android-native.mdx +++ b/docs/getting-started/android-native.mdx @@ -24,10 +24,10 @@ repositories { } dependencies { - debugImplementation 'com.facebook.flipper:flipper:0.255.0' + debugImplementation 'com.facebook.flipper:flipper:0.256.0' debugImplementation 'com.facebook.soloader:soloader:0.10.5' - releaseImplementation 'com.facebook.flipper:flipper-noop:0.255.0' + releaseImplementation 'com.facebook.flipper:flipper-noop:0.256.0' } ``` diff --git a/docs/getting-started/react-native-ios.mdx b/docs/getting-started/react-native-ios.mdx index 2d0deb47428..ee062efbc1c 100644 --- a/docs/getting-started/react-native-ios.mdx +++ b/docs/getting-started/react-native-ios.mdx @@ -51,7 +51,7 @@ Add all of the code below to your `ios/Podfile`: platform :ios, '9.0' def flipper_pods() - flipperkit_version = '0.255.0' # should match the version of your Flipper client app + flipperkit_version = '0.256.0' # should match the version of your Flipper client app pod 'FlipperKit', '~>' + flipperkit_version, :configuration => 'Debug' pod 'FlipperKit/FlipperKitLayoutPlugin', '~>' + flipperkit_version, :configuration => 'Debug' pod 'FlipperKit/SKIOSNetworkPlugin', '~>' + flipperkit_version, :configuration => 'Debug' diff --git a/docs/getting-started/react-native.mdx b/docs/getting-started/react-native.mdx index 6f19400b85e..d11383fbf25 100644 --- a/docs/getting-started/react-native.mdx +++ b/docs/getting-started/react-native.mdx @@ -34,7 +34,7 @@ Latest version of Flipper requires react-native 0.69+! If you use react-native < Android: -1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.255.0`. +1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.256.0`. 2. Run `./gradlew clean` in the `android` directory. iOS: @@ -44,7 +44,7 @@ react-native version => 0.69.0 2. Run `pod install --repo-update` in the `ios` directory. react-native version < 0.69.0 -1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.255.0' })`. +1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.256.0' })`. 2. Run `pod install --repo-update` in the `ios` directory. ## Manual Setup diff --git a/gradle.properties b/gradle.properties index fab5bb28860..6b004a07026 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. # POM publishing constants -VERSION_NAME=0.255.1-SNAPSHOT +VERSION_NAME=0.256.0 GROUP=com.facebook.flipper SONATYPE_STAGING_PROFILE=comfacebook POM_URL=https://github.com/facebook/flipper diff --git a/js/js-flipper/package.json b/js/js-flipper/package.json index 486e2932517..f7dc1997074 100644 --- a/js/js-flipper/package.json +++ b/js/js-flipper/package.json @@ -1,7 +1,7 @@ { "name": "js-flipper", "title": "JS Flipper Bindings for Web-Socket based clients", - "version": "0.255.0", + "version": "0.256.0", "main": "lib/index.js", "browser": { "os": false diff --git a/react-native/react-native-flipper/package.json b/react-native/react-native-flipper/package.json index 280d967bb0c..9f43cdc3359 100644 --- a/react-native/react-native-flipper/package.json +++ b/react-native/react-native-flipper/package.json @@ -1,7 +1,7 @@ { "name": "react-native-flipper", "title": "React Native Flipper Bindings", - "version": "0.255.0", + "version": "0.256.0", "description": "Flipper bindings for React Native", "main": "index.js", "types": "index.d.ts", From 2ecc987b176243eb46cce7cf0d4b6c1f5ab86231 Mon Sep 17 00:00:00 2001 From: generatedunixname89002005306973 <generatedunixname89002005306973@meta.com> Date: Wed, 19 Jun 2024 05:16:16 -0700 Subject: [PATCH 72/92] Flipper Snapshot Bump: v0.256.1-SNAPSHOT Summary: Releasing snapshot version 0.256.1-SNAPSHOT Reviewed By: mweststrate Differential Revision: D58777923 fbshipit-source-id: f97d3a8a3db145c5bd4bc707a3d656341bd75bdd --- docs/getting-started/android-native.mdx | 4 ++-- gradle.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/android-native.mdx b/docs/getting-started/android-native.mdx index d3f6473e807..5554b893ad5 100644 --- a/docs/getting-started/android-native.mdx +++ b/docs/getting-started/android-native.mdx @@ -124,10 +124,10 @@ repositories { } dependencies { - debugImplementation 'com.facebook.flipper:flipper:0.255.1-SNAPSHOT' + debugImplementation 'com.facebook.flipper:flipper:0.256.1-SNAPSHOT' debugImplementation 'com.facebook.soloader:soloader:0.10.5' - releaseImplementation 'com.facebook.flipper:flipper-noop:0.255.1-SNAPSHOT' + releaseImplementation 'com.facebook.flipper:flipper-noop:0.256.1-SNAPSHOT' } ``` diff --git a/gradle.properties b/gradle.properties index 6b004a07026..1f380bf966d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. # POM publishing constants -VERSION_NAME=0.256.0 +VERSION_NAME=0.256.1-SNAPSHOT GROUP=com.facebook.flipper SONATYPE_STAGING_PROFILE=comfacebook POM_URL=https://github.com/facebook/flipper From 042e1f863383a87d06208ba7b62b57bff351c3c8 Mon Sep 17 00:00:00 2001 From: Pascal Hartig <realpassy@meta.com> Date: Wed, 19 Jun 2024 07:33:03 -0700 Subject: [PATCH 73/92] Edit marketplace.mdx using inpage editor Summary: This diff has been automatically generated by the inpage editor. NOTE: If you want to update this diff, go via the preview link inside the static docs section below. Ensure you are editing the same page that was used to create this diff. Reviewed By: LukeDefeo Differential Revision: D58784177 fbshipit-source-id: 7e23eb1ea7fbb4aa854c8de84c9095b696ebefbb --- docs/tutorial/marketplace.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/marketplace.mdx b/docs/tutorial/marketplace.mdx index 18f992f088a..5279413b991 100644 --- a/docs/tutorial/marketplace.mdx +++ b/docs/tutorial/marketplace.mdx @@ -17,7 +17,7 @@ This significantly help users to discover useful plugins for their app. Once plugin is installed, it will also keep updating to latest version. -## Enable markektplace +## Enable Marketplace ### Setup server From da66d6eb7056ccc8c4b4026a07686e7b5d9062eb Mon Sep 17 00:00:00 2001 From: Fabio Carballo <fabiocarballo@meta.com> Date: Fri, 21 Jun 2024 03:19:32 -0700 Subject: [PATCH 74/92] Whitelist another Activity so that we can inspect the content Summary: Enabling us to debug GenAI bottom sheet for WWAI. Reviewed By: AlexBalo Differential Revision: D58817360 fbshipit-source-id: a42f0f77b2202888310569783eef415828ca2556 --- .../plugins/uidebugger/descriptors/ApplicationRefDescriptor.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/ApplicationRefDescriptor.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/ApplicationRefDescriptor.kt index ededb39477e..b250b20a17b 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/ApplicationRefDescriptor.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/ApplicationRefDescriptor.kt @@ -62,7 +62,8 @@ object ApplicationRefDescriptor : ChainedDescriptor<ApplicationRef>() { val className = rootViewOrActivity.javaClass.name if (className.contains("mediagallery.ui.MediaGalleryActivity") || - className.contains("ImagineCreationActivity")) { + className.contains("ImagineCreationActivity") || + className.contains("WriteWithAIActivity")) { // this activity doesn't contain the content and its actually in the decor view behind it, so // skip it :/ return false From e19a711ca77bf8251933dee07203ec771c612d0f Mon Sep 17 00:00:00 2001 From: Pascal Hartig <realpassy@meta.com> Date: Fri, 21 Jun 2024 07:15:24 -0700 Subject: [PATCH 75/92] Unify path checking Summary: We already check for `flipper://` above, so we can focus on the `pathname`. Reviewed By: antonk52 Differential Revision: D58811471 fbshipit-source-id: ab7b7adafa21a9a0e7acc512c0cbf9c3204db2e2 --- desktop/flipper-ui/src/deeplink.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/flipper-ui/src/deeplink.tsx b/desktop/flipper-ui/src/deeplink.tsx index 4dee6d2f9d5..bbbd43a9dfa 100644 --- a/desktop/flipper-ui/src/deeplink.tsx +++ b/desktop/flipper-ui/src/deeplink.tsx @@ -47,7 +47,7 @@ export async function handleDeeplink( // or alternatively flipper://welcome to open the welcome screen. return; } - if (uri.href.startsWith('flipper://open-plugin')) { + if (uri.pathname === '//open-plugin') { return handleOpenPluginDeeplink(store, query, trackInteraction); } if (uri.pathname.match(/^\/*import\/*$/)) { From 3b0f7eece247f5aa68d317ab079f61901ba92d98 Mon Sep 17 00:00:00 2001 From: Garrett Steffen <garrettsteffen@meta.com> Date: Fri, 21 Jun 2024 12:48:11 -0700 Subject: [PATCH 76/92] background execution flipper plugin add basic charting, update client Summary: {F1707823594} {F1710112208} Differential Revision: D58210819 fbshipit-source-id: baac3b3aea9b9790b258a5eb049ce97e140398e0 --- desktop/flipper-plugin/src/data-source/DataSource.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/flipper-plugin/src/data-source/DataSource.tsx b/desktop/flipper-plugin/src/data-source/DataSource.tsx index c2abe6daec5..c8b0ef5dde3 100644 --- a/desktop/flipper-plugin/src/data-source/DataSource.tsx +++ b/desktop/flipper-plugin/src/data-source/DataSource.tsx @@ -1128,7 +1128,7 @@ export class DataSourceView<T, KeyType> { output, { value: oldValue, - id: -1, + id: -99999, visible: entry.visible, approxIndex: entry.approxIndex, }, From 2464fe7901a23018301c2b9c96551d1a0bb839da Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin <hoxy@meta.com> Date: Tue, 25 Jun 2024 10:52:46 -0700 Subject: [PATCH 77/92] upgrade[react-devtools-*]: v.5.3.0 Summary: X-link: https://github.com/facebook/react-native/pull/45159 Changelog: [Internal] via `js1 upgrade react-devtools -v ^5.3.0` allow-large-files Reviewed By: EdmondChuiHW Differential Revision: D59001348 fbshipit-source-id: e17e1070b38256644fa987bd00510b8a7c5d848a --- .../plugins/public/reactdevtools/package.json | 4 ++-- desktop/plugins/public/yarn.lock | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/desktop/plugins/public/reactdevtools/package.json b/desktop/plugins/public/reactdevtools/package.json index dccb88d2b33..4d9be947648 100644 --- a/desktop/plugins/public/reactdevtools/package.json +++ b/desktop/plugins/public/reactdevtools/package.json @@ -21,8 +21,8 @@ "dependencies": { "@rollup/plugin-commonjs": "^21.0.3", "@rollup/plugin-node-resolve": "^13.1.3", - "react-devtools-core": "5.1.0", - "react-devtools-inline": "5.1.0", + "react-devtools-core": "^5.3.0", + "react-devtools-inline": "^5.3.0", "rollup": "^2.70.1", "ws": "^8.5.0" }, diff --git a/desktop/plugins/public/yarn.lock b/desktop/plugins/public/yarn.lock index a35624a055a..65dc52c1107 100644 --- a/desktop/plugins/public/yarn.lock +++ b/desktop/plugins/public/yarn.lock @@ -1889,18 +1889,18 @@ react-color@^2.19.3: reactcss "^1.2.0" tinycolor2 "^1.4.1" -react-devtools-core@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-5.1.0.tgz#3396494ac94b21602cac4fd657d600e0b52f4a0b" - integrity sha512-NRtLBqYVLrIY+lOa2oTpFiAhI7Hru0AUXI0tP9neCyaPPAzlZyeH0i+VZ0shIyRTJbpvyqbD/uCsewA2hpfZHw== +react-devtools-core@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-5.3.0.tgz#8062c25a4707c3216333270210bfe06871f7eed4" + integrity sha512-IG3T+azv48Oc5VLdHR4XdBNKNZIUOKRtx0sJMRvb++Zom/uqtx73j6u37JCsIBNIaq6vA7RPH5Bbcf/Wj53KXA== dependencies: shell-quote "^1.6.1" ws "^7" -react-devtools-inline@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/react-devtools-inline/-/react-devtools-inline-5.1.0.tgz#1a32cafd5f73328f809449d1c7add8dbf1fb8325" - integrity sha512-E5NJiIzItjETGFLVem0cN732yfT9Zij8stNPWyztL7pvKeWN0sNVYSMenitjyeYyue54Az21M4oicnm95tm5JQ== +react-devtools-inline@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/react-devtools-inline/-/react-devtools-inline-5.3.0.tgz#b11049f90ef29849d9f1c344d0b8fc89b01f4234" + integrity sha512-FwSPcqXXaWee++LtH5UsO+r3CgNP7kin6B0fsuTeUFO9TQieWIDX8GiF6D5MNn4UsX6A36j+KPVZNDFlyQBGjw== dependencies: source-map-js "^0.6.2" sourcemap-codec "^1.4.8" From 7121a3b7c2e636d39e356fba11edbca4775e7a1e Mon Sep 17 00:00:00 2001 From: Luke De Feo <lukedefeo@meta.com> Date: Wed, 26 Jun 2024 09:57:40 -0700 Subject: [PATCH 78/92] Improve box visualser by indicating in tree which components have box model data Summary: it was unclear which elements had padding / margin before so this should help with that Reviewed By: antonk52 Differential Revision: D59061383 fbshipit-source-id: 6c92dcd7e086ddc537ab8d9a40a0fecc15dc92b8 --- .../public/ui-debugger/DesktopTypes.tsx | 2 + .../ui-debugger/components/tree/Tree.tsx | 41 ++++++++++++++++++- .../components/tree/TreeControls.tsx | 1 + .../components/visualizer/Visualization2D.tsx | 5 +-- .../visualizer/VisualizerControls.tsx | 3 -- desktop/plugins/public/ui-debugger/index.tsx | 2 +- .../public/ui-debugger/plugin/uiActions.tsx | 8 ++++ 7 files changed, 53 insertions(+), 9 deletions(-) diff --git a/desktop/plugins/public/ui-debugger/DesktopTypes.tsx b/desktop/plugins/public/ui-debugger/DesktopTypes.tsx index c023a4c615c..567a53e20e0 100644 --- a/desktop/plugins/public/ui-debugger/DesktopTypes.tsx +++ b/desktop/plugins/public/ui-debugger/DesktopTypes.tsx @@ -34,6 +34,7 @@ export type Color = string; export type UIState = { viewMode: Atom<ViewMode>; wireFrameMode: Atom<WireFrameMode>; + boxVisualiserEnabled: Atom<boolean>; isConnected: Atom<boolean>; isPaused: Atom<boolean>; streamState: Atom<StreamState>; @@ -120,6 +121,7 @@ export type UIActions = { setVisualiserWidth: (width: number) => void; onSetFilterMainThreadMonitoring: (toggled: boolean) => void; onSetViewMode: (viewMode: ViewMode) => void; + onSetBoxVisualiserEnabled: (enabled: boolean) => void; onSetFrameworkEventMonitored: ( eventType: FrameworkEventType, monitored: boolean, diff --git a/desktop/plugins/public/ui-debugger/components/tree/Tree.tsx b/desktop/plugins/public/ui-debugger/components/tree/Tree.tsx index 62f87715053..c8be774172a 100644 --- a/desktop/plugins/public/ui-debugger/components/tree/Tree.tsx +++ b/desktop/plugins/public/ui-debugger/components/tree/Tree.tsx @@ -7,7 +7,14 @@ * @format */ -import {Id, ClientNode, NodeMap, MetadataId, Metadata} from '../../ClientTypes'; +import { + Id, + ClientNode, + NodeMap, + MetadataId, + Metadata, + BoxData, +} from '../../ClientTypes'; import {Color, OnSelectNode} from '../../DesktopTypes'; import React, { CSSProperties, @@ -39,7 +46,11 @@ import { useKeyboardControlsCallback, } from './useKeyboardControls'; import {toTreeList} from './toTreeList'; -import {CaretDownOutlined, WarningOutlined} from '@ant-design/icons'; +import { + BorderOutlined, + CaretDownOutlined, + WarningOutlined, +} from '@ant-design/icons'; const {Text} = Typography; @@ -74,6 +85,9 @@ export function Tree2({ const nodeSelection = useValue(instance.uiState.nodeSelection); const isContextMenuOpen = useValue(instance.uiState.isContextMenuOpen); const hoveredNode = head(useValue(instance.uiState.hoveredNodes)); + const isBoxVisualiserEnabled = useValue( + instance.uiState.boxVisualiserEnabled, + ); const filterMainThreadMonitoring = useValue( instance.uiState.filterMainThreadMonitoring, @@ -290,6 +304,7 @@ export function Tree2({ innerRef={refs[virtualRow.index]} key={virtualRow.index} treeNode={treeNodes[virtualRow.index]} + boxVisualiserEnabled={isBoxVisualiserEnabled} highlightedNodes={highlightedNodes} selectedNode={nodeSelection?.node.id} hoveredNode={hoveredNode} @@ -399,12 +414,14 @@ export function TreeNodeRow({ onExpandNode, onCollapseNode, onHoverNode, + boxVisualiserEnabled, }: { transform: string; innerRef: Ref<any>; treeNode: TreeNode; highlightedNodes: Map<Id, Color>; selectedNode?: Id; + boxVisualiserEnabled: boolean; hoveredNode?: Id; isUsingKBToScroll: RefObject<MillisSinceEpoch>; isContextMenuOpen: boolean; @@ -470,6 +487,11 @@ export function TreeNodeRow({ {nodeIcon(treeNode)} <TreeNodeTextContent treeNode={treeNode} /> + {boxVisualiserEnabled && boxDataHasData(treeNode.boxData) && ( + <Tooltip title="This element has padding, margin or border"> + <BorderOutlined style={{padding: theme.space.medium}} /> + </Tooltip> + )} {treeNode.frameworkEvents && ( <Tooltip title={`${treeNode.frameworkEvents} monitored framework events`}> @@ -486,6 +508,21 @@ export function TreeNodeRow({ </div> ); } +function boxDataHasData(boxData?: BoxData) { + if (boxData == null) { + return false; + } + for (let i = 0; i < boxData.border.length - 1; i++) { + if ( + boxData.border[i] > 0 || + boxData.padding[i] > 0 || + boxData.margin[i] > 0 + ) { + return true; + } + } + return false; +} function TreeNodeTextContent({treeNode}: {treeNode: TreeNode}) { const isZero = treeNode.bounds.width === 0 && treeNode.bounds.height === 0; diff --git a/desktop/plugins/public/ui-debugger/components/tree/TreeControls.tsx b/desktop/plugins/public/ui-debugger/components/tree/TreeControls.tsx index 12911cf7cc8..9d0d5697697 100644 --- a/desktop/plugins/public/ui-debugger/components/tree/TreeControls.tsx +++ b/desktop/plugins/public/ui-debugger/components/tree/TreeControls.tsx @@ -267,6 +267,7 @@ function FrameworkEventsMonitoringModal({ <div style={{position: 'relative', height: 26, marginTop: 16}}> <TreeNodeRow + boxVisualiserEnabled={false} transform="" onCollapseNode={() => {}} onExpandNode={() => {}} diff --git a/desktop/plugins/public/ui-debugger/components/visualizer/Visualization2D.tsx b/desktop/plugins/public/ui-debugger/components/visualizer/Visualization2D.tsx index 17a062621e3..41cbd016d3f 100644 --- a/desktop/plugins/public/ui-debugger/components/visualizer/Visualization2D.tsx +++ b/desktop/plugins/public/ui-debugger/components/visualizer/Visualization2D.tsx @@ -53,9 +53,8 @@ export const Visualization2D: React.FC< const focusedNodeId = useValue(instance.uiState.focusedNode); const nodeSelection = useValue(instance.uiState.nodeSelection); const wireFrameMode = useValue(instance.uiState.wireFrameMode); - + const boxVisualiserEnabled = useValue(instance.uiState.boxVisualiserEnabled); const [alignmentModeEnabled, setAlignmentModeEnabled] = useState(false); - const [boxVisualiserEnabled, setBoxVisualiserEnabled] = useState(false); const [targetMode, setTargetMode] = useState<TargetModeState>({ state: 'disabled', @@ -83,7 +82,7 @@ export const Visualization2D: React.FC< alignmentModeEnabled={alignmentModeEnabled} setAlignmentModeEnabled={setAlignmentModeEnabled} boxVisualiserEnabled={boxVisualiserEnabled} - setBoxVisualiserEnabled={setBoxVisualiserEnabled} + setBoxVisualiserEnabled={instance.uiActions.onSetBoxVisualiserEnabled} /> )} <Visualization2DContent diff --git a/desktop/plugins/public/ui-debugger/components/visualizer/VisualizerControls.tsx b/desktop/plugins/public/ui-debugger/components/visualizer/VisualizerControls.tsx index 2ff037357e2..50c213f1406 100644 --- a/desktop/plugins/public/ui-debugger/components/visualizer/VisualizerControls.tsx +++ b/desktop/plugins/public/ui-debugger/components/visualizer/VisualizerControls.tsx @@ -130,9 +130,6 @@ export function VisualiserControls({ disabled={boxModeDisabled} shape="circle" onClick={() => { - tracker.track('box-visualiser-switched', { - on: !boxVisualiserEnabled, - }); setBoxVisualiserEnabled(!boxVisualiserEnabled); }} icon={ diff --git a/desktop/plugins/public/ui-debugger/index.tsx b/desktop/plugins/public/ui-debugger/index.tsx index 24418a07495..54ee6ddf939 100644 --- a/desktop/plugins/public/ui-debugger/index.tsx +++ b/desktop/plugins/public/ui-debugger/index.tsx @@ -343,7 +343,7 @@ export * from './ClientTypes'; function createUIState(): UIState { return { isConnected: createState(false), - + boxVisualiserEnabled: createState(false), viewMode: createState({mode: 'default'}), //used to disabled hover effects which cause rerenders and mess up the existing context menu diff --git a/desktop/plugins/public/ui-debugger/plugin/uiActions.tsx b/desktop/plugins/public/ui-debugger/plugin/uiActions.tsx index d5d004a462a..c39a338feeb 100644 --- a/desktop/plugins/public/ui-debugger/plugin/uiActions.tsx +++ b/desktop/plugins/public/ui-debugger/plugin/uiActions.tsx @@ -311,9 +311,17 @@ export function uiActions( }); }; + const onSetBoxVisualiserEnabled = (enabled: boolean) => { + uiState.boxVisualiserEnabled.set(enabled); + tracker.track('box-visualiser-switched', { + on: enabled, + }); + }; + return { onExpandNode, onCollapseNode, + onSetBoxVisualiserEnabled, onHoverNode, onSelectNode, onContextMenuOpen, From 122fcae6ee00204e88671834ea49e1ae06d4856e Mon Sep 17 00:00:00 2001 From: Pascal Hartig <realpassy@meta.com> Date: Thu, 27 Jun 2024 05:08:59 -0700 Subject: [PATCH 79/92] Improve restart message Summary: The full stop at the end of the dialogue looks out of place: {F1725727234} Reviewed By: antonk52 Differential Revision: D59108252 fbshipit-source-id: 8eb6fd4ab92fad661ab52709680a69027c2c90de --- desktop/flipper-ui/src/chrome/UpdateIndicator.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/desktop/flipper-ui/src/chrome/UpdateIndicator.tsx b/desktop/flipper-ui/src/chrome/UpdateIndicator.tsx index d693db1d97b..f65bba87af0 100644 --- a/desktop/flipper-ui/src/chrome/UpdateIndicator.tsx +++ b/desktop/flipper-ui/src/chrome/UpdateIndicator.tsx @@ -146,11 +146,10 @@ export function getUpdateAvailableMessage(versionCheckResult: { <> {' '} Run <code>arc pull</code> (optionally with <code>--latest</code>) in{' '} - <code>~/fbsource</code> and{' '} + <code>~/fbsource</code>, then{' '} <Button block type="primary" onClick={shutdownFlipper}> Quit Flipper to upgrade </Button> - . </> ) ) : ( From b3c8ba8efafd973d999c722052c5c1dc70cf20f9 Mon Sep 17 00:00:00 2001 From: Luke De Feo <lukedefeo@meta.com> Date: Fri, 28 Jun 2024 00:59:16 -0700 Subject: [PATCH 80/92] build dev build overwrite error Summary: some users and myself were facing a strange issue in development getting the error from esbuild errors: [ { detail: undefined, id: '', location: null, notes: [], pluginName: '', text: 'Refusing to overwrite input file "../plugins/public/ui-debugger/dist/bundle.js" (use "allowOverwrite: true" to allow this)' } ], the overwrite flag is set so its likley a bug in a dependency somewhere, rather than chasign it down this seems to work fixes https://fb.workplace.com/groups/flippersupport/permalink/1862902277523755/ Reviewed By: mweststrate Differential Revision: D59145649 fbshipit-source-id: f795ad7bb20bff20fd3b9084fc31f6fa2e21b6bb --- desktop/pkg-lib/src/runBuild.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/desktop/pkg-lib/src/runBuild.tsx b/desktop/pkg-lib/src/runBuild.tsx index 4f300af8abd..e07e14b4b86 100644 --- a/desktop/pkg-lib/src/runBuild.tsx +++ b/desktop/pkg-lib/src/runBuild.tsx @@ -154,6 +154,8 @@ export default async function bundlePlugin( const bundleConfigs: RunBuildConfig[] = []; + await fs.remove(path.dirname(plugin.entry)); + await fs.ensureDir(path.dirname(plugin.entry)); bundleConfigs.push({ pluginDir, From 67817cb490b706c62e42129baebc1296f5a449c6 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy <antonk52@meta.com> Date: Fri, 28 Jun 2024 05:18:58 -0700 Subject: [PATCH 81/92] remove unused code Reviewed By: LukeDefeo Differential Revision: D59118053 fbshipit-source-id: f7baed412f2f6a5530cb9f2617c95694694ba492 --- desktop/doctor/src/index.tsx | 47 ------------------------------------ 1 file changed, 47 deletions(-) diff --git a/desktop/doctor/src/index.tsx b/desktop/doctor/src/index.tsx index 660581a3dac..5311dfdb550 100644 --- a/desktop/doctor/src/index.tsx +++ b/desktop/doctor/src/index.tsx @@ -10,7 +10,6 @@ import {exec} from 'child_process'; import os from 'os'; import {promisify} from 'util'; -import {getEnvInfo} from './environmentInfo'; export {getEnvInfo} from './environmentInfo'; import * as watchman from 'fb-watchman'; @@ -428,52 +427,6 @@ export function getHealthchecks( }; } -export async function runHealthchecks( - isProduction: boolean, -): Promise< - Array<FlipperDoctor.CategoryResult | FlipperDoctor.SkippedHealthcheckCategory> -> { - const environmentInfo = await getEnvInfo(); - const healthchecks: FlipperDoctor.Healthchecks = - getHealthchecks(isProduction); - const results: Array< - FlipperDoctor.CategoryResult | FlipperDoctor.SkippedHealthcheckCategory - > = await Promise.all( - Object.entries(healthchecks).map(async ([key, category]) => { - if (category.isSkipped) { - return category; - } - const categoryResult: FlipperDoctor.CategoryResult = [ - key, - { - label: category.label, - results: await Promise.all( - category.healthchecks.map( - async ({key, label, run, isRequired}) => ({ - key, - label, - isRequired: isRequired ?? true, - // TODO: Fix this the next time the file is edited. - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - result: await run!(environmentInfo).catch((e) => { - console.warn(`Health check ${key}/${label} failed with:`, e); - // TODO Improve result type to be: OK | Problem(message, fix...) - return { - hasProblem: true, - }; - }), - }), - ), - ), - }, - ]; - - return categoryResult; - }), - ); - return results; -} - async function tryExecuteCommand( command: string, ): Promise< From d9258eb755c9a06962ea86e7bea3b39bebd34d19 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy <antonk52@meta.com> Date: Fri, 28 Jun 2024 09:14:40 -0700 Subject: [PATCH 82/92] fix idb_companion detection Summary: * `idb_companion` is snake case * `idb_companion --help` exists with non-zero exit code, thus using `which` instead Reviewed By: LukeDefeo Differential Revision: D59162166 fbshipit-source-id: 0d4917339a846493735804155af8178a904f15e8 --- desktop/doctor/src/index.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/desktop/doctor/src/index.tsx b/desktop/doctor/src/index.tsx index 5311dfdb550..d0fcc5ff582 100644 --- a/desktop/doctor/src/index.tsx +++ b/desktop/doctor/src/index.tsx @@ -188,8 +188,6 @@ export function getHealthchecks( const result = await tryExecuteCommand( `${settings?.idbPath} --help`, ); - const hasIdbCompanion = - await tryExecuteCommand(`idbCompanion --help`); if (result.fail) { const hasIdbInPath = await tryExecuteCommand(`which idb`); @@ -205,6 +203,9 @@ export function getHealthchecks( ], }; } + const hasIdbCompanion = await tryExecuteCommand( + 'which idb_companion', + ); return { hasProblem: true, From 875c0874e9b8d735a0b31abc95e94d4dd802cda7 Mon Sep 17 00:00:00 2001 From: Luke De Feo <lukedefeo@meta.com> Date: Fri, 28 Jun 2024 10:28:33 -0700 Subject: [PATCH 83/92] Prevent selected node from auto expanding when new frame come in Summary: addresses https://fb.workplace.com/groups/flippersupport/permalink/1864127004067949/ on receiving a frame we call a method ensureAncestorsExpanded on the selected node. If the user has collapsed the tree containing the selected node this causes it to re-expand on the next frame. this is a bit janky given that when a frame comes in there might be no user interaction (videos playing / animations trigger new frames) I cant think of a good reason this expansion needs to occur so opted to remove it Reviewed By: antonk52 Differential Revision: D59163209 fbshipit-source-id: cf91401fa811d372ec85853e03c04bd4da4ad542 --- desktop/plugins/public/ui-debugger/index.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/desktop/plugins/public/ui-debugger/index.tsx b/desktop/plugins/public/ui-debugger/index.tsx index 54ee6ddf939..61f19e1afef 100644 --- a/desktop/plugins/public/ui-debugger/index.tsx +++ b/desktop/plugins/public/ui-debugger/index.tsx @@ -185,9 +185,6 @@ export function plugin(client: PluginClient<Events, Methods>) { if (frame.frameTime > lastProcessedFrameTime.get()) { applyFrameData(frame.nodes, frame.snapshot); lastProcessedFrameTime.set(frame.frameTime); - const selectedNode = uiState.nodeSelection.get(); - if (selectedNode != null) - _uiActions.ensureAncestorsExpanded(selectedNode.node.id); } }); From 7a2fb7bef7ba5fdea21e528e182da788df22cb43 Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy <antonk52@meta.com> Date: Mon, 1 Jul 2024 03:35:14 -0700 Subject: [PATCH 84/92] replace ts-node with tsx Summary: As part of simplifying codebase we can remove a custom command to run ts-node in favor of tsx(transforms typescript using esbuild instead of tsc). Might be marginal speed difference which is irrelevant. Main reason is that it provides an easier way to run typescript code on all platforms. We already use esbuild to bundle plugins so this fits nicely. Reviewed By: mweststrate Differential Revision: D59109777 fbshipit-source-id: 3dbf06552aadf0e2c5a873a6d73e1aa425800f02 --- desktop/babel-transformer/package.json | 2 +- desktop/examples/flipper-dump/package.json | 2 +- desktop/examples/flipper-dump/tsconfig.json | 5 +- desktop/package.json | 25 +-- desktop/ts-node.cmd | 8 - desktop/yarn.lock | 166 ++++++++++++++++++++ 6 files changed, 182 insertions(+), 26 deletions(-) delete mode 100644 desktop/ts-node.cmd diff --git a/desktop/babel-transformer/package.json b/desktop/babel-transformer/package.json index 8bb9d8a721d..a246a167e3f 100644 --- a/desktop/babel-transformer/package.json +++ b/desktop/babel-transformer/package.json @@ -29,7 +29,7 @@ }, "scripts": { "reset": "rimraf lib *.tsbuildinfo", - "build": "tsc -b && cd .. && ./ts-node ./scripts/compute-package-checksum.tsx -d ./babel-transformer -o ./lib/checksum.txt", + "build": "tsc -b && cd .. && tsx ./scripts/compute-package-checksum.tsx -d ./babel-transformer -o ./lib/checksum.txt", "prepack": "yarn reset && yarn build" }, "files": [ diff --git a/desktop/examples/flipper-dump/package.json b/desktop/examples/flipper-dump/package.json index ca98f73c975..08f6c1cd465 100644 --- a/desktop/examples/flipper-dump/package.json +++ b/desktop/examples/flipper-dump/package.json @@ -19,7 +19,7 @@ }, "peerDependencies": {}, "scripts": { - "start": "ts-node src/index.tsx", + "start": "tsx src/index.tsx", "reset": "rimraf lib *.tsbuildinfo", "build": "tsc -b", "prepack": "yarn reset && yarn build" diff --git a/desktop/examples/flipper-dump/tsconfig.json b/desktop/examples/flipper-dump/tsconfig.json index 03395b02e0e..21659acb212 100644 --- a/desktop/examples/flipper-dump/tsconfig.json +++ b/desktop/examples/flipper-dump/tsconfig.json @@ -11,8 +11,5 @@ { "path": "../../flipper-common" } - ], - "ts-node": { - "transpileOnly": true - } + ] } diff --git a/desktop/package.json b/desktop/package.json index c1bd7c83a9c..b551969f665 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -101,6 +101,7 @@ "rimraf": "^3.0.2", "ts-jest": "^29.1.0", "ts-node": "^10.9.1", + "tsx": "^4.15.7", "typescript": "^5.4.2" }, "homepage": "https://fbflipper.com/", @@ -119,32 +120,32 @@ }, "scripts": { "build": "yarn build:flipper-server", - "build-plugin": "./ts-node scripts/build-plugin.tsx", + "build-plugin": "tsx scripts/build-plugin.tsx", "build:eslint": "cd eslint-plugin-flipper && yarn build", - "build:flipper-server": "yarn build:tsc && ./ts-node scripts/build-flipper-server-release.tsx", + "build:flipper-server": "yarn build:tsc && tsx scripts/build-flipper-server-release.tsx", "build:themes": "lessc --js themes/light.less static/themes/light.css && lessc --js themes/dark.less static/themes/dark.css", - "build:tsc": "tsc -b tsc-root/tsconfig.json && ./ts-node ./scripts/compute-package-checksum.tsx -d ./babel-transformer -o ./lib/checksum.txt", - "bump-versions": "./ts-node scripts/bump-versions.tsx", - "bundle-all-plugins": "./ts-node scripts/bundle-all-plugins.tsx", + "build:tsc": "tsc -b tsc-root/tsconfig.json && tsx ./scripts/compute-package-checksum.tsx -d ./babel-transformer -o ./lib/checksum.txt", + "bump-versions": "tsx scripts/bump-versions.tsx", + "bundle-all-plugins": "tsx scripts/bundle-all-plugins.tsx", "docs": "cd ../website && yarn start", "fix": "eslint . --fix --ext .js,.ts,.tsx", - "flipper-server": "cross-env NODE_ENV=development ./ts-node scripts/start-flipper-server-dev.tsx", + "flipper-server": "cross-env NODE_ENV=development tsx scripts/start-flipper-server-dev.tsx", "lint": "yarn lint:eslint && yarn lint:tsc && yarn tsc-plugins && yarn run lint:types-deps", "lint:eslint": "eslint . --ext .js,.ts,.tsx", "lint:tsc": "tsc && tsc -p tsc-root/tsconfig.json --noemit", - "lint:types-deps": "./ts-node ./scripts/verify-types-dependencies.tsx", - "list-plugins": "./ts-node scripts/list-plugins.tsx", + "lint:types-deps": "tsx ./scripts/verify-types-dependencies.tsx", + "list-plugins": "tsx scripts/list-plugins.tsx", "open-dist": "open ../dist/mac/Flipper.app --args --launcher=false --inspect=9229", - "postinstall": "patch-package && yarn --cwd plugins install --mutex network:30331 && yarn tsc -b pkg-lib/tsconfig.json && ./ts-node scripts/remove-plugin-entry-points.tsx && yarn build:tsc && yarn build:themes", + "postinstall": "patch-package && yarn --cwd plugins install --mutex network:30331 && yarn tsc -b pkg-lib/tsconfig.json && tsx scripts/remove-plugin-entry-points.tsx && yarn build:tsc && yarn build:themes", "prebuild": "yarn build:tsc && yarn rm-dist && yarn build:themes", "predev-server": "yarn build:tsc", "preflipper-server": "yarn build:tsc", "preinstall": "node scripts/prepare-watchman-config.js && yarn config set ignore-engines", "prelint:eslint": "yarn build:eslint", "pretest": "yarn build:tsc", - "publish-packages": "./ts-node scripts/publish-packages.tsx", + "publish-packages": "tsx scripts/publish-packages.tsx", "reset": "yarn rm-dist && yarn rm-temp && yarn rm-metro-cache && yarn cache clean && yarn rm-bundle && yarn rm-modules", - "resolve-plugin-dir": "./ts-node scripts/resolve-plugin-dir.tsx", + "resolve-plugin-dir": "tsx scripts/resolve-plugin-dir.tsx", "rm-bundle": "rimraf static/main.bundle.* **/dist/bundle.* **/lib **/*.tsbuildinfo", "rm-dist": "rimraf ../dist", "rm-metro-cache": "rimraf $TMPDIR/metro-cache*", @@ -156,7 +157,7 @@ "start:no-bundled-plugins": "yarn start --no-bundled-plugins", "test": "cross-env TZ=Pacific/Pohnpei jest", "test:debug": "yarn build:tsc && cross-env TZ=Pacific/Pohnpei node --inspect node_modules/.bin/jest --runInBand", - "tsc-plugins": "./ts-node scripts/tsc-plugins.tsx", + "tsc-plugins": "tsx scripts/tsc-plugins.tsx", "watch": "cross-env TZ=Pacific/Pohnpei node --expose-gc --stack-trace-limit=40 ./node_modules/.bin/jest --watch" }, "engines": { diff --git a/desktop/ts-node.cmd b/desktop/ts-node.cmd deleted file mode 100644 index 442b468b8eb..00000000000 --- a/desktop/ts-node.cmd +++ /dev/null @@ -1,8 +0,0 @@ -@REM Copyright (c) Meta Platforms, Inc. and affiliates. -@REM -@REM This source code is licensed under the MIT license found in the -@REM LICENSE file in the root directory of this source tree. - -@echo off - -set TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=scripts/tsconfig.json & node --max_old_space_size=4096 --require ts-node/register %* diff --git a/desktop/yarn.lock b/desktop/yarn.lock index 87e14902f0f..ede69c47ce4 100644 --- a/desktop/yarn.lock +++ b/desktop/yarn.lock @@ -2287,16 +2287,131 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== +"@esbuild/aix-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" + integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== + +"@esbuild/android-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" + integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== + "@esbuild/android-arm@0.15.18": version "0.15.18" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.18.tgz#266d40b8fdcf87962df8af05b76219bc786b4f80" integrity sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw== +"@esbuild/android-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" + integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== + +"@esbuild/android-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" + integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== + +"@esbuild/darwin-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" + integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + +"@esbuild/darwin-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" + integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== + +"@esbuild/freebsd-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" + integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== + +"@esbuild/freebsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" + integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== + +"@esbuild/linux-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" + integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== + +"@esbuild/linux-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" + integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== + +"@esbuild/linux-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" + integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== + "@esbuild/linux-loong64@0.15.18": version "0.15.18" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz#128b76ecb9be48b60cf5cfc1c63a4f00691a3239" integrity sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ== +"@esbuild/linux-loong64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" + integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== + +"@esbuild/linux-mips64el@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" + integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== + +"@esbuild/linux-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" + integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== + +"@esbuild/linux-riscv64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" + integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== + +"@esbuild/linux-s390x@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" + integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== + +"@esbuild/linux-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" + integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== + +"@esbuild/netbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" + integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== + +"@esbuild/openbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" + integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== + +"@esbuild/sunos-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" + integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== + +"@esbuild/win32-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" + integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== + +"@esbuild/win32-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" + integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== + +"@esbuild/win32-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" + integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -6159,6 +6274,35 @@ esbuild@^0.15.18: esbuild-windows-64 "0.15.18" esbuild-windows-arm64 "0.15.18" +esbuild@~0.21.4: + version "0.21.5" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" + integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== + optionalDependencies: + "@esbuild/aix-ppc64" "0.21.5" + "@esbuild/android-arm" "0.21.5" + "@esbuild/android-arm64" "0.21.5" + "@esbuild/android-x64" "0.21.5" + "@esbuild/darwin-arm64" "0.21.5" + "@esbuild/darwin-x64" "0.21.5" + "@esbuild/freebsd-arm64" "0.21.5" + "@esbuild/freebsd-x64" "0.21.5" + "@esbuild/linux-arm" "0.21.5" + "@esbuild/linux-arm64" "0.21.5" + "@esbuild/linux-ia32" "0.21.5" + "@esbuild/linux-loong64" "0.21.5" + "@esbuild/linux-mips64el" "0.21.5" + "@esbuild/linux-ppc64" "0.21.5" + "@esbuild/linux-riscv64" "0.21.5" + "@esbuild/linux-s390x" "0.21.5" + "@esbuild/linux-x64" "0.21.5" + "@esbuild/netbsd-x64" "0.21.5" + "@esbuild/openbsd-x64" "0.21.5" + "@esbuild/sunos-x64" "0.21.5" + "@esbuild/win32-arm64" "0.21.5" + "@esbuild/win32-ia32" "0.21.5" + "@esbuild/win32-x64" "0.21.5" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -7038,6 +7182,11 @@ fsevents@^2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== +fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -7138,6 +7287,13 @@ get-tsconfig@^4.5.0: dependencies: resolve-pkg-maps "^1.0.0" +get-tsconfig@^4.7.5: + version "4.7.5" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.5.tgz#5e012498579e9a6947511ed0cd403272c7acbbaf" + integrity sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw== + dependencies: + resolve-pkg-maps "^1.0.0" + github-slugger@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.3.0.tgz#9bd0a95c5efdfc46005e82a906ef8e2a059124c9" @@ -12558,6 +12714,16 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" +tsx@^4.15.7: + version "4.15.7" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.15.7.tgz#69d7499196a323507c4051d2ba10753edcc057e5" + integrity sha512-u3H0iSFDZM3za+VxkZ1kywdCeHCn+8/qHQS1MNoO2sONDgD95HlWtt8aB23OzeTmFP9IU4/8bZUdg58Uu5J4cg== + dependencies: + esbuild "~0.21.4" + get-tsconfig "^4.7.5" + optionalDependencies: + fsevents "~2.3.3" + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" From a05cb667d03094a11e1ff3bcee386445981245da Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy <antonk52@meta.com> Date: Mon, 1 Jul 2024 07:13:08 -0700 Subject: [PATCH 85/92] remove doctor cli Summary: Removing unused cli from doctor. This unblocks landing doctor into flipper server Reviewed By: LukeDefeo Differential Revision: D59220594 fbshipit-source-id: d7b683a42d039406fd2ced7c7f9d4925efabb57b --- desktop/doctor/package.json | 3 +-- desktop/doctor/src/cli.tsx | 38 ------------------------------------- 2 files changed, 1 insertion(+), 40 deletions(-) delete mode 100644 desktop/doctor/src/cli.tsx diff --git a/desktop/doctor/package.json b/desktop/doctor/package.json index 3d728f0882b..2c703e4e3da 100644 --- a/desktop/doctor/package.json +++ b/desktop/doctor/package.json @@ -13,8 +13,7 @@ "scripts": { "reset": "rimraf lib *.tsbuildinfo", "build": "tsc -b", - "prepack": "yarn reset && yarn build", - "run": "yarn run build && node lib/cli.js" + "prepack": "yarn reset && yarn build" }, "files": [ "lib/**/*" diff --git a/desktop/doctor/src/cli.tsx b/desktop/doctor/src/cli.tsx deleted file mode 100644 index dd1b1089776..00000000000 --- a/desktop/doctor/src/cli.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -import {getHealthchecks} from './index'; -import {getEnvInfo} from './environmentInfo'; - -(async () => { - const environmentInfo = await getEnvInfo(); - console.log(JSON.stringify(environmentInfo)); - const healthchecks = getHealthchecks(false); - const results = await Promise.all( - Object.entries(healthchecks).map(async ([key, category]) => [ - key, - category.isSkipped - ? category - : { - label: category.label, - results: await Promise.all( - category.healthchecks.map(async ({key, label, run}) => ({ - key, - label, - // TODO: Fix this the next time the file is edited. - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - result: await run!(environmentInfo), - })), - ), - }, - ]), - ); - - console.log(JSON.stringify(results, null, 2)); -})(); From a17d3d6716e13c509746e5c807b350164d04b90e Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy <antonk52@meta.com> Date: Mon, 1 Jul 2024 07:13:08 -0700 Subject: [PATCH 86/92] merge doctor into flipper-server Summary: * hg move doctor/src to flipper-server/src/doctor * update imports * move dependencies from doctor/package.json to flipper-server/package.json * remove doctor * remove mentions of doctor from build/tsconfigs Reviewed By: LukeDefeo Differential Revision: D59220610 fbshipit-source-id: 9f44c2a6be510b28552b43ae7e0bbb1cb024d0f2 --- desktop/doctor/.eslintrc.js | 14 -------- desktop/doctor/README.md | 8 ----- desktop/doctor/jestconfig.json | 7 ---- desktop/doctor/package.json | 32 ------------------- desktop/doctor/tsconfig.json | 13 -------- desktop/doctor/tslint.json | 3 -- desktop/flipper-server/package.json | 4 ++- .../src/doctor}/environmentInfo.tsx | 0 .../fb-stubs/validateSelectedXcodeVersion.tsx | 0 .../src/doctor}/globals.d.ts | 0 .../src/doctor}/index.tsx | 3 ++ .../src/utils/runHealthchecks.tsx | 2 +- desktop/flipper-server/tsconfig.json | 3 -- desktop/jest.config.js | 2 +- desktop/package.json | 1 - .../scripts/build-flipper-server-release.tsx | 1 - desktop/scripts/start-flipper-server-dev.tsx | 8 +---- desktop/tsc-root/tsconfig.json | 3 -- desktop/yarn.lock | 10 +++--- 19 files changed, 15 insertions(+), 99 deletions(-) delete mode 100644 desktop/doctor/.eslintrc.js delete mode 100644 desktop/doctor/README.md delete mode 100644 desktop/doctor/jestconfig.json delete mode 100644 desktop/doctor/package.json delete mode 100644 desktop/doctor/tsconfig.json delete mode 100644 desktop/doctor/tslint.json rename desktop/{doctor/src => flipper-server/src/doctor}/environmentInfo.tsx (100%) rename desktop/{doctor/src => flipper-server/src/doctor}/fb-stubs/validateSelectedXcodeVersion.tsx (100%) rename desktop/{doctor/src => flipper-server/src/doctor}/globals.d.ts (100%) rename desktop/{doctor/src => flipper-server/src/doctor}/index.tsx (98%) diff --git a/desktop/doctor/.eslintrc.js b/desktop/doctor/.eslintrc.js deleted file mode 100644 index cabcfb08b3c..00000000000 --- a/desktop/doctor/.eslintrc.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - */ - -module.exports = { - rules: { - 'node/no-sync': 'off', - }, -}; diff --git a/desktop/doctor/README.md b/desktop/doctor/README.md deleted file mode 100644 index 74cab895579..00000000000 --- a/desktop/doctor/README.md +++ /dev/null @@ -1,8 +0,0 @@ -# Flipper Doctor - -This package exists for running checks to diagnose and potentially fix issues affecting the operation of Flipper. -It's designed to be primarily used programmatically but may also expose a CLI interface. - -## Usage -`cd doctor` -`yarn run run` diff --git a/desktop/doctor/jestconfig.json b/desktop/doctor/jestconfig.json deleted file mode 100644 index 20c25c0f71d..00000000000 --- a/desktop/doctor/jestconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "transform": { - "^.+\\.(t|j)sx?$": "ts-jest" - }, - "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", - "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"] -} diff --git a/desktop/doctor/package.json b/desktop/doctor/package.json deleted file mode 100644 index 2c703e4e3da..00000000000 --- a/desktop/doctor/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "flipper-doctor", - "version": "0.0.0", - "description": "Utility for checking for issues with a flipper installation", - "main": "lib/index.js", - "flipperBundlerEntry": "src", - "types": "lib/index.d.ts", - "license": "MIT", - "devDependencies": { - "@types/fb-watchman": "2.0.1", - "@types/node": "^17.0.31" - }, - "scripts": { - "reset": "rimraf lib *.tsbuildinfo", - "build": "tsc -b", - "prepack": "yarn reset && yarn build" - }, - "files": [ - "lib/**/*" - ], - "keywords": [ - "Flipper", - "Doctor" - ], - "author": "Facebook, Inc", - "dependencies": { - "envinfo": "^7.8.1", - "fb-watchman": "^2.0.2", - "flipper-common": "0.0.0", - "fs-extra": "^11.1.1" - } -} diff --git a/desktop/doctor/tsconfig.json b/desktop/doctor/tsconfig.json deleted file mode 100644 index 195fa4fb690..00000000000 --- a/desktop/doctor/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../tsconfig.base.json", - "compilerOptions": { - "outDir": "lib", - "rootDir": "src", - "types": ["node"] - }, - "references": [ - { - "path": "../flipper-common" - } - ] -} diff --git a/desktop/doctor/tslint.json b/desktop/doctor/tslint.json deleted file mode 100644 index 85e60a40a1c..00000000000 --- a/desktop/doctor/tslint.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": ["tslint:recommended", "tslint-config-prettier"] -} diff --git a/desktop/flipper-server/package.json b/desktop/flipper-server/package.json index 7f3549d5aa9..b602c98d9f2 100644 --- a/desktop/flipper-server/package.json +++ b/desktop/flipper-server/package.json @@ -10,8 +10,10 @@ "bugs": "https://github.com/facebook/flipper/issues", "dependencies": { "chalk": "^4", + "envinfo": "^7.8.1", "exit-hook": "^2.1.1", "express": "^4.17.3", + "fb-watchman": "^2.0.2", "file-stream-rotator": "^0.6.1", "flipper-common": "0.0.0", "flipper-pkg-lib": "0.0.0", @@ -31,7 +33,6 @@ "archiver": "^5.3.1", "async-mutex": "^0.3.2", "axios": "^0.26.0", - "flipper-doctor": "0.0.0", "flipper-plugin-lib": "0.0.0", "form-data": "^4.0.0", "invariant": "^2.2.4", @@ -56,6 +57,7 @@ }, "devDependencies": { "@types/express": "^4.17.13", + "@types/fb-watchman": "^2.0.1", "@types/http-proxy": "^1.17.8", "@types/node": "^17.0.31", "@types/archiver": "^5.3.1", diff --git a/desktop/doctor/src/environmentInfo.tsx b/desktop/flipper-server/src/doctor/environmentInfo.tsx similarity index 100% rename from desktop/doctor/src/environmentInfo.tsx rename to desktop/flipper-server/src/doctor/environmentInfo.tsx diff --git a/desktop/doctor/src/fb-stubs/validateSelectedXcodeVersion.tsx b/desktop/flipper-server/src/doctor/fb-stubs/validateSelectedXcodeVersion.tsx similarity index 100% rename from desktop/doctor/src/fb-stubs/validateSelectedXcodeVersion.tsx rename to desktop/flipper-server/src/doctor/fb-stubs/validateSelectedXcodeVersion.tsx diff --git a/desktop/doctor/src/globals.d.ts b/desktop/flipper-server/src/doctor/globals.d.ts similarity index 100% rename from desktop/doctor/src/globals.d.ts rename to desktop/flipper-server/src/doctor/globals.d.ts diff --git a/desktop/doctor/src/index.tsx b/desktop/flipper-server/src/doctor/index.tsx similarity index 98% rename from desktop/doctor/src/index.tsx rename to desktop/flipper-server/src/doctor/index.tsx index d0fcc5ff582..f74bdbb1cb3 100644 --- a/desktop/doctor/src/index.tsx +++ b/desktop/flipper-server/src/doctor/index.tsx @@ -75,6 +75,7 @@ export function getHealthchecks( run: async ( _: FlipperDoctor.EnvironmentInfo, ): Promise<FlipperDoctor.HealthcheckRunResult> => { + // eslint-disable-next-line node/no-sync const hasProblem = !fs.existsSync( '/Applications/Android Studio.app', ); @@ -106,6 +107,7 @@ export function getHealthchecks( hasProblem: true, message: ['android.sdk--no_ANDROID_HOME'], }; + // eslint-disable-next-line node/no-sync } else if (!fs.existsSync(androidHome)) { const androidStudioAndroidHome = `${os.homedir()}/Library/Android/sdk`; const globalAndroidHome = '/opt/android_sdk'; @@ -125,6 +127,7 @@ export function getHealthchecks( }; } else { const platformToolsDir = path.join(androidHome, 'platform-tools'); + // eslint-disable-next-line node/no-sync if (!fs.existsSync(platformToolsDir)) { return { hasProblem: true, diff --git a/desktop/flipper-server/src/utils/runHealthchecks.tsx b/desktop/flipper-server/src/utils/runHealthchecks.tsx index 728ce170680..6be98f1fc03 100644 --- a/desktop/flipper-server/src/utils/runHealthchecks.tsx +++ b/desktop/flipper-server/src/utils/runHealthchecks.tsx @@ -7,7 +7,7 @@ * @format */ -import {getHealthchecks, getEnvInfo} from 'flipper-doctor'; +import {getHealthchecks, getEnvInfo} from '../doctor'; import {FlipperDoctor} from 'flipper-common'; import produce from 'immer'; diff --git a/desktop/flipper-server/tsconfig.json b/desktop/flipper-server/tsconfig.json index 1d2ef734773..045fca6a95d 100644 --- a/desktop/flipper-server/tsconfig.json +++ b/desktop/flipper-server/tsconfig.json @@ -16,9 +16,6 @@ }, "include": ["./src/**/*"], "references": [ - { - "path": "../doctor" - }, { "path": "../flipper-common" }, diff --git a/desktop/jest.config.js b/desktop/jest.config.js index 4b0aa921902..3926aa8616c 100644 --- a/desktop/jest.config.js +++ b/desktop/jest.config.js @@ -19,7 +19,7 @@ module.exports = { '^flipper-plugin$': '<rootDir>/flipper-plugin/src', '^flipper-(server-core|ui-core|frontend-core|common)$': '<rootDir>/flipper-$1/src', - '^flipper-(pkg|pkg-lib|doctor|test-utils)$': '<rootDir>/$1/src', + '^flipper-(pkg|pkg-lib|test-utils)$': '<rootDir>/$1/src', '^.+\\.(css|scss)$': '<rootDir>/scripts/jest-css-stub.js', }, clearMocks: true, diff --git a/desktop/package.json b/desktop/package.json index b551969f665..1f3ccd59aaf 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -170,7 +170,6 @@ "packages": [ "scripts", "babel-transformer", - "doctor", "pkg", "pkg-lib", "flipper-common", diff --git a/desktop/scripts/build-flipper-server-release.tsx b/desktop/scripts/build-flipper-server-release.tsx index 2a015c3bd86..c0b455ca7d7 100644 --- a/desktop/scripts/build-flipper-server-release.tsx +++ b/desktop/scripts/build-flipper-server-release.tsx @@ -270,7 +270,6 @@ async function linkLocalDeps(buildFolder: string) { const resolutions = { ...manifest.resolutions, - 'flipper-doctor': `file:${rootDir}/doctor`, 'flipper-common': `file:${rootDir}/flipper-common`, 'flipper-server-client': `file:${rootDir}/flipper-server-client`, 'flipper-pkg-lib': `file:${rootDir}/pkg-lib`, diff --git a/desktop/scripts/start-flipper-server-dev.tsx b/desktop/scripts/start-flipper-server-dev.tsx index 721eed61591..b5074fe3f43 100644 --- a/desktop/scripts/start-flipper-server-dev.tsx +++ b/desktop/scripts/start-flipper-server-dev.tsx @@ -129,13 +129,7 @@ async function startWatchChanges() { // We only watch for changes that might affect the server. // For UI changes, Metro / hot module reloading / fast refresh take care of the changes await Promise.all( - [ - 'doctor', - 'pkg-lib', - 'plugin-lib', - 'flipper-common', - 'flipper-server', - ].map((dir) => + ['pkg-lib', 'plugin-lib', 'flipper-common', 'flipper-server'].map((dir) => watchman.startWatchFiles( dir, () => { diff --git a/desktop/tsc-root/tsconfig.json b/desktop/tsc-root/tsconfig.json index 4909cf70855..2813cb948b1 100644 --- a/desktop/tsc-root/tsconfig.json +++ b/desktop/tsc-root/tsconfig.json @@ -30,9 +30,6 @@ { "path": "../pkg-lib" }, - { - "path": "../doctor" - }, { "path": "../eslint-plugin-flipper" }, diff --git a/desktop/yarn.lock b/desktop/yarn.lock index ede69c47ce4..a6bab64a0a4 100644 --- a/desktop/yarn.lock +++ b/desktop/yarn.lock @@ -3369,10 +3369,12 @@ "@types/qs" "*" "@types/serve-static" "*" -"@types/fb-watchman@2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/fb-watchman/-/fb-watchman-2.0.1.tgz#4f44c7b7ac98463488765d41d269272dd34cb815" - integrity sha512-iJ7/e6drSmuCzAp96/dpksm8YjxbhhyXWV6m1HPbRHvZwUOUZ5vZvZIAUJxKDtI0UpdNfDvLPiai0MTJmmS+HA== +"@types/fb-watchman@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/fb-watchman/-/fb-watchman-2.0.4.tgz#3a02379aa90d86ceb965a2beaebd7e79faa81e82" + integrity sha512-h7yAQ5Sf96PdZz+sfDDl6Hscl/JqsVXadjF6YHmJeWviziDCGQtqUQUDS2tYjfD/eTzhfZwNfwcfb91a4PaZKQ== + dependencies: + "@types/node" "*" "@types/file-saver@^2.0.5": version "2.0.5" From 8c1cbe1f0484124ed78e106733d117c30bca138b Mon Sep 17 00:00:00 2001 From: Anton Kastritskiy <antonk52@meta.com> Date: Mon, 1 Jul 2024 08:11:41 -0700 Subject: [PATCH 87/92] surface subchecks for failed xcode-select command Summary: The subchecks are displayed for failed command only. Rationale: it is tricky to help people when the command fails. Surfacing subchecks should make it easier for users as well as flipper oncall to see why a check failed and what subchecks have passed Reviewed By: LukeDefeo Differential Revision: D59222954 fbshipit-source-id: dfb77b4b2d0b6facca609d6e4e9bbe59d67d9a77 --- desktop/flipper-common/src/doctor.tsx | 8 +++++ .../fb-stubs/validateSelectedXcodeVersion.tsx | 1 + desktop/flipper-server/src/doctor/index.tsx | 36 +++++++++++++++++-- .../src/utils/runHealthchecks.tsx | 1 + .../src/sandy-chrome/SetupDoctorScreen.tsx | 26 +++++++++++--- 5 files changed, 65 insertions(+), 7 deletions(-) diff --git a/desktop/flipper-common/src/doctor.tsx b/desktop/flipper-common/src/doctor.tsx index cc444bed1b2..a05522b6c40 100644 --- a/desktop/flipper-common/src/doctor.tsx +++ b/desktop/flipper-common/src/doctor.tsx @@ -69,8 +69,15 @@ export namespace FlipperDoctor { command: string; }; + export type HealthcheckRunSubcheck = { + status: 'ok' | 'fail'; + title: string; + }; + export type HealthcheckRunResult = { hasProblem: boolean; + /** Indicates what sub checks were passed to better communicate the problem */ + subchecks?: HealthcheckRunSubcheck[]; message: MessageIdWithParams; }; @@ -109,6 +116,7 @@ export namespace FlipperDoctor { status: HealthcheckStatus; isAcknowledged?: boolean; message?: MessageIdWithParams; + subchecks?: HealthcheckRunSubcheck[]; }; export type HealthcheckReportItem = { diff --git a/desktop/flipper-server/src/doctor/fb-stubs/validateSelectedXcodeVersion.tsx b/desktop/flipper-server/src/doctor/fb-stubs/validateSelectedXcodeVersion.tsx index 1b5ca7d1e4f..5ac03187519 100644 --- a/desktop/flipper-server/src/doctor/fb-stubs/validateSelectedXcodeVersion.tsx +++ b/desktop/flipper-server/src/doctor/fb-stubs/validateSelectedXcodeVersion.tsx @@ -12,6 +12,7 @@ import {FlipperDoctor} from 'flipper-common'; export async function validateSelectedXcodeVersion( _selectedPath: string, _availableXcode: string | null, + _subchecks: FlipperDoctor.HealthcheckRunSubcheck[], ): Promise<FlipperDoctor.HealthcheckRunResult> { return { hasProblem: false, diff --git a/desktop/flipper-server/src/doctor/index.tsx b/desktop/flipper-server/src/doctor/index.tsx index f74bdbb1cb3..24dbc09fe9f 100644 --- a/desktop/flipper-server/src/doctor/index.tsx +++ b/desktop/flipper-server/src/doctor/index.tsx @@ -261,6 +261,7 @@ export function getHealthchecks( run: async ( _: FlipperDoctor.EnvironmentInfo, ): Promise<FlipperDoctor.HealthcheckRunResult> => { + const subchecks: FlipperDoctor.HealthcheckRunSubcheck[] = []; const allApps = await fs_extra.promises.readdir('/Applications'); // Xcode_14.2.0_xxxxxxx.app @@ -274,11 +275,20 @@ export function getHealthchecks( const availableXcode = latestXCode ? path.join('/Applications', latestXCode) : null; + subchecks.push({ + status: availableXcode ? 'ok' : 'fail', + title: 'Xcode in /Applications', + }); const result = await tryExecuteCommand('xcode-select -p'); + subchecks.push({ + status: result.fail ? 'fail' : 'ok', + title: 'xcode-select runs successfully', + }); if (result.fail) { return { hasProblem: true, + subchecks, message: [ 'ios.xcode-select--not_set', {message: result.message, availableXcode}, @@ -287,18 +297,34 @@ export function getHealthchecks( } const selectedXcode = result.stdout.toString().trim(); - if (selectedXcode == '/Library/Developer/CommandLineTools') { + const isSelectedXcodeCommandLineTools = + selectedXcode == '/Library/Developer/CommandLineTools'; + subchecks.push({ + status: isSelectedXcodeCommandLineTools ? 'fail' : 'ok', + title: + 'xcode-select does NOT point to "/Library/Developer/CommandLineTools"', + }); + if (isSelectedXcodeCommandLineTools) { return { hasProblem: true, + subchecks, message: [ 'ios.xcode-select--no_xcode_selected', {availableXcode}, ], }; } - if ((await fs_extra.pathExists(selectedXcode)) == false) { + + const selectedXcodeExists = + await fs_extra.pathExists(selectedXcode); + subchecks.push({ + status: selectedXcodeExists ? 'ok' : 'fail', + title: 'Selected Xcode exists', + }); + if (!selectedXcodeExists) { return { hasProblem: true, + subchecks, message: [ 'ios.xcode-select--nonexisting_selected', {selected: selectedXcode, availableXcode}, @@ -309,9 +335,13 @@ export function getHealthchecks( await validateSelectedXcodeVersion( selectedXcode, availableXcode, + subchecks, ); if (validatedXcodeVersion.hasProblem) { - return validatedXcodeVersion; + return { + ...validatedXcodeVersion, + subchecks, + }; } return { hasProblem: false, diff --git a/desktop/flipper-server/src/utils/runHealthchecks.tsx b/desktop/flipper-server/src/utils/runHealthchecks.tsx index 6be98f1fc03..22f4362e80d 100644 --- a/desktop/flipper-server/src/utils/runHealthchecks.tsx +++ b/desktop/flipper-server/src/utils/runHealthchecks.tsx @@ -68,6 +68,7 @@ export async function runHealthcheck( return checkResult.hasProblem && check.isRequired ? { status: 'FAILED', + subchecks: checkResult.subchecks, message: checkResult.message, } : checkResult.hasProblem && !check.isRequired diff --git a/desktop/flipper-ui/src/sandy-chrome/SetupDoctorScreen.tsx b/desktop/flipper-ui/src/sandy-chrome/SetupDoctorScreen.tsx index bb1953d9cd7..f33e4d76f7f 100644 --- a/desktop/flipper-ui/src/sandy-chrome/SetupDoctorScreen.tsx +++ b/desktop/flipper-ui/src/sandy-chrome/SetupDoctorScreen.tsx @@ -154,10 +154,28 @@ function CollapsableCategory(props: { header={check.label} extra={<CheckIcon status={check.result.status} />}> {check.result.message != null ? ( - <DoctorMessage - id={check.result.message[0]} - props={check.result.message[1]} - /> + <> + {check.result.subchecks ? ( + <ul> + {check.result.subchecks.map((subcheck, i) => ( + <li key={i}> + {subcheck.status === 'ok' ? ( + <CheckCircleFilled + style={{color: theme.successColor}} + /> + ) : ( + <CloseCircleFilled style={{color: theme.errorColor}} /> + )}{' '} + {subcheck.title} + </li> + ))} + </ul> + ) : null} + <DoctorMessage + id={check.result.message[0]} + props={check.result.message[1]} + /> + </> ) : null} </Collapse.Panel> ))} From 0dc81e35e2f84a7302ceabbd4c8c17cafe896707 Mon Sep 17 00:00:00 2001 From: Ruslan Lesiutin <hoxy@meta.com> Date: Thu, 4 Jul 2024 16:02:07 -0700 Subject: [PATCH 88/92] upgrade[react-devtools-*]: v.5.3.1 Summary: X-link: https://github.com/facebook/react-native/pull/45291 Changelog: [Internal] Same as D59001348, via `js1 upgrade react-devtools -v ^5.3.1` allow-large-files Reviewed By: vzaidman Differential Revision: D59374023 fbshipit-source-id: 7493000bff24a5e21cb77a9a5992c501ed282a92 --- .../plugins/public/reactdevtools/package.json | 4 ++-- desktop/plugins/public/yarn.lock | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/desktop/plugins/public/reactdevtools/package.json b/desktop/plugins/public/reactdevtools/package.json index 4d9be947648..17effa230f7 100644 --- a/desktop/plugins/public/reactdevtools/package.json +++ b/desktop/plugins/public/reactdevtools/package.json @@ -21,8 +21,8 @@ "dependencies": { "@rollup/plugin-commonjs": "^21.0.3", "@rollup/plugin-node-resolve": "^13.1.3", - "react-devtools-core": "^5.3.0", - "react-devtools-inline": "^5.3.0", + "react-devtools-core": "^5.3.1", + "react-devtools-inline": "^5.3.1", "rollup": "^2.70.1", "ws": "^8.5.0" }, diff --git a/desktop/plugins/public/yarn.lock b/desktop/plugins/public/yarn.lock index 65dc52c1107..87f3cf1f818 100644 --- a/desktop/plugins/public/yarn.lock +++ b/desktop/plugins/public/yarn.lock @@ -1889,18 +1889,18 @@ react-color@^2.19.3: reactcss "^1.2.0" tinycolor2 "^1.4.1" -react-devtools-core@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-5.3.0.tgz#8062c25a4707c3216333270210bfe06871f7eed4" - integrity sha512-IG3T+azv48Oc5VLdHR4XdBNKNZIUOKRtx0sJMRvb++Zom/uqtx73j6u37JCsIBNIaq6vA7RPH5Bbcf/Wj53KXA== +react-devtools-core@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-5.3.1.tgz#d57f5b8f74f16e622bd6a7bc270161e4ba162666" + integrity sha512-7FSb9meX0btdBQLwdFOwt6bGqvRPabmVMMslv8fgoSPqXyuGpgQe36kx8gR86XPw7aV1yVouTp6fyZ0EH+NfUw== dependencies: shell-quote "^1.6.1" ws "^7" -react-devtools-inline@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/react-devtools-inline/-/react-devtools-inline-5.3.0.tgz#b11049f90ef29849d9f1c344d0b8fc89b01f4234" - integrity sha512-FwSPcqXXaWee++LtH5UsO+r3CgNP7kin6B0fsuTeUFO9TQieWIDX8GiF6D5MNn4UsX6A36j+KPVZNDFlyQBGjw== +react-devtools-inline@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/react-devtools-inline/-/react-devtools-inline-5.3.1.tgz#91aba25796804cc0993320f93805ed291ced0ee7" + integrity sha512-f5DJJg4p+3SvUQt/Yw5KiZinOBOGwF1yumWla875lO5qch1m/IFNkZqRRcmboPQKp2L4aqtVd/hyyEJVu642Xw== dependencies: source-map-js "^0.6.2" sourcemap-codec "^1.4.8" From b6f8125ac26322f7d49b94fb222b8fd330411022 Mon Sep 17 00:00:00 2001 From: Dominik Pich <dpich@meta.com> Date: Mon, 8 Jul 2024 08:03:49 -0700 Subject: [PATCH 89/92] also observe WKHTTPCookieStore Summary: make the flipper plugin also observe WKHTTPCookieStore (see https://fb.workplace.com/groups/flipper.community.support/permalink/1031948131877035/) -- this entailed: 1. doing all on main thread(WKWebkitStorage isnt fully thread safe) 2. diffing the changes as the 2 storages refresh too often without changes (especially for ~~10secs after app start while storages are setup) Reviewed By: lblasa Differential Revision: D59422745 fbshipit-source-id: d4756766428588aaf16e9cab334c9e1d8912e3b5 --- .../Exported/FlipperKitCookiesPlugin.mm | 101 ++++++++++++++---- 1 file changed, 83 insertions(+), 18 deletions(-) diff --git a/iOS/Plugins/FlipperKitCookiesPlugin/FlipperKitCookiesPlugin/Exported/FlipperKitCookiesPlugin.mm b/iOS/Plugins/FlipperKitCookiesPlugin/FlipperKitCookiesPlugin/Exported/FlipperKitCookiesPlugin.mm index 86da7671cc8..675f3a375e9 100644 --- a/iOS/Plugins/FlipperKitCookiesPlugin/FlipperKitCookiesPlugin/Exported/FlipperKitCookiesPlugin.mm +++ b/iOS/Plugins/FlipperKitCookiesPlugin/FlipperKitCookiesPlugin/Exported/FlipperKitCookiesPlugin.mm @@ -7,14 +7,20 @@ #ifdef FB_SONARKIT_ENABLED -#import "FlipperKitCookiesPlugin.h" +#import <WebKit/WebKit.h> + #import <FlipperKit/FlipperClient.h> #import <FlipperKit/FlipperConnection.h> #import <FlipperKit/FlipperResponder.h> +#import "FlipperKitCookiesPlugin.h" #import "Plugins.h" +@interface FlipperKitCookiesPlugin ()<WKHTTPCookieStoreObserver> +@end + @implementation FlipperKitCookiesPlugin { id<FlipperConnection> _connection; + NSArray<NSHTTPCookie*>* _previousCookies; } - (NSString*)identifier { @@ -22,44 +28,103 @@ - (NSString*)identifier { } - (void)didConnect:(id<FlipperConnection>)connection { - _connection = connection; - [self _sendCookies]; + __weak __typeof(self) weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + [weakSelf _didConnect:connection]; + }); +} + +- (void)didDisconnect { + __weak __typeof(self) weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + [weakSelf _didDisconnect]; + }); +} + +#pragma mark - NSHTTP Cookie storage observer + +- (void)onNSHTTPCookieStorageChange:(NSNotification*)notification { + __weak __typeof(self) weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + [weakSelf _refreshCookies]; + }); +} + +#pragma mark - WKHTTPCookieStoreObserver + +- (void)cookiesDidChangeInCookieStore:(WKHTTPCookieStore*)cookieStore { + __weak __typeof(self) weakSelf = self; + dispatch_async(dispatch_get_main_queue(), ^{ + [weakSelf _refreshCookies]; + }); +} + +#pragma mark - helper + +- (void)_didConnect:(id<FlipperConnection>)connection { + self->_connection = connection; [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(onCookieStorageChange:) + selector:@selector(onNSHTTPCookieStorageChange:) name:NSHTTPCookieManagerCookiesChangedNotification object:nil]; + [[[WKWebsiteDataStore defaultDataStore] httpCookieStore] addObserver:self]; + [self _refreshCookies]; } -- (void)didDisconnect { +- (void)_didDisconnect { + self->_connection = nil; [[NSNotificationCenter defaultCenter] removeObserver:self]; - _connection = nil; + [[[WKWebsiteDataStore defaultDataStore] httpCookieStore] removeObserver:self]; + _previousCookies = nil; } -#pragma mark - cookie storage observer +- (void)_refreshCookies { + // get Cookies from NS + NSArray<NSHTTPCookie*>* _Nullable NSCookies = + [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]; + + // get Cookies from WebKit + [[[WKWebsiteDataStore defaultDataStore] httpCookieStore] + getAllCookies:^(NSArray<NSHTTPCookie*>* WKCookies) { + // combine and sort cookies based on name - preferring WK + NSMutableDictionary<NSString*, NSHTTPCookie*>* cookies = + [NSMutableDictionary dictionary]; + for (NSHTTPCookie* cookie in NSCookies) { + cookies[cookie.name] = cookie; + } + for (NSHTTPCookie* cookie in WKCookies) { + cookies[cookie.name] = cookie; + } + NSArray<NSHTTPCookie*>* sortedCookies = [cookies.allValues + sortedArrayUsingComparator:^NSComparisonResult( + NSHTTPCookie* cookie1, NSHTTPCookie* cookie2) { + return [cookie1.name compare:cookie2.name]; + }]; -- (void)onCookieStorageChange:(NSNotification*)notification { - [self _sendCookies]; + [self _sendCookies:sortedCookies]; + }]; } -#pragma mark - helper +- (void)_sendCookies:(NSArray*)sortedCookies { + if ([_previousCookies isEqualToArray:sortedCookies]) { + return; + } -- (void)_sendCookies { + // deliver to flipper [_connection send:@"resetCookies" withParams:@{}]; - - NSInteger i = 1; - NSArray<NSHTTPCookie*>* _Nullable cookies = - [[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies] copy]; - for (NSHTTPCookie* cookie in cookies) { + NSInteger rowid = 1; + for (NSHTTPCookie* cookie in sortedCookies) { NSMutableDictionary<NSString*, id>* dict = [NSMutableDictionary dictionary]; - dict[@"id"] = @(i); + dict[@"id"] = @(rowid++); dict[@"Name"] = cookie.name; dict[@"Expires"] = cookie.expiresDate.description; dict[@"Value"] = cookie.value; [_connection send:@"addCookie" withParams:dict]; - i++; } + + _previousCookies = sortedCookies.copy; } @end From 063e959d909829ff578f755e9068a500c884fd11 Mon Sep 17 00:00:00 2001 From: generatedunixname89002005306973 <generatedunixname89002005306973@meta.com> Date: Wed, 10 Jul 2024 05:57:08 -0700 Subject: [PATCH 90/92] Flipper Release: v0.257.0 Summary: Releasing version 0.257.0 Reviewed By: antonk52 Differential Revision: D59577587 fbshipit-source-id: d4aa180349e172dec7aaaa0fa9ad4ef2be716268 --- desktop/package.json | 2 +- desktop/plugins/public/layout/docs/setup.mdx | 2 +- desktop/plugins/public/leak_canary/docs/setup.mdx | 2 +- desktop/plugins/public/network/docs/setup.mdx | 2 +- desktop/static/CHANGELOG.md | 6 ++++++ docs/getting-started/android-native.mdx | 4 ++-- docs/getting-started/react-native-ios.mdx | 2 +- docs/getting-started/react-native.mdx | 4 ++-- gradle.properties | 2 +- js/js-flipper/package.json | 2 +- react-native/react-native-flipper/package.json | 2 +- 11 files changed, 18 insertions(+), 12 deletions(-) diff --git a/desktop/package.json b/desktop/package.json index 1f3ccd59aaf..6c043b53a39 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -165,7 +165,7 @@ "npm": "use yarn instead", "yarn": "^1.16" }, - "version": "0.256.0", + "version": "0.257.0", "workspaces": { "packages": [ "scripts", diff --git a/desktop/plugins/public/layout/docs/setup.mdx b/desktop/plugins/public/layout/docs/setup.mdx index 7cde52dfd08..4b49b8a9c56 100644 --- a/desktop/plugins/public/layout/docs/setup.mdx +++ b/desktop/plugins/public/layout/docs/setup.mdx @@ -27,7 +27,7 @@ You also need to compile in the `litho-annotations` package, as Flipper reflects ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.256.0' + debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.257.0' debugImplementation 'com.facebook.litho:litho-annotations:0.19.0' // ... } diff --git a/desktop/plugins/public/leak_canary/docs/setup.mdx b/desktop/plugins/public/leak_canary/docs/setup.mdx index 92d198cc07c..0a272114d4c 100644 --- a/desktop/plugins/public/leak_canary/docs/setup.mdx +++ b/desktop/plugins/public/leak_canary/docs/setup.mdx @@ -8,7 +8,7 @@ To setup the <Link to={useBaseUrl("/docs/features/plugins/leak-canary")}>LeakCan ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.256.0' + debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.257.0' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1' } ``` diff --git a/desktop/plugins/public/network/docs/setup.mdx b/desktop/plugins/public/network/docs/setup.mdx index 0e345c86d99..12e154e3fa7 100644 --- a/desktop/plugins/public/network/docs/setup.mdx +++ b/desktop/plugins/public/network/docs/setup.mdx @@ -11,7 +11,7 @@ The network plugin is shipped as a separate Maven artifact, as follows: ```groovy dependencies { - debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.256.0' + debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.257.0' } ``` diff --git a/desktop/static/CHANGELOG.md b/desktop/static/CHANGELOG.md index e71ad4a4995..d593f9aaf7d 100644 --- a/desktop/static/CHANGELOG.md +++ b/desktop/static/CHANGELOG.md @@ -1,3 +1,9 @@ +# 0.257.0 (10/7/2024) + + * [D59001348](https://github.com/facebook/flipper/search?q=D59001348&type=Commits) - [Internal] + * [D59374023](https://github.com/facebook/flipper/search?q=D59374023&type=Commits) - [Internal] + + # 0.255.0 (12/6/2024) * [D57865621](https://github.com/facebook/flipper/search?q=D57865621&type=Commits) - Network plugin: Store data in IndexedDB to reduce memory consumption diff --git a/docs/getting-started/android-native.mdx b/docs/getting-started/android-native.mdx index 5554b893ad5..3794ecd3722 100644 --- a/docs/getting-started/android-native.mdx +++ b/docs/getting-started/android-native.mdx @@ -24,10 +24,10 @@ repositories { } dependencies { - debugImplementation 'com.facebook.flipper:flipper:0.256.0' + debugImplementation 'com.facebook.flipper:flipper:0.257.0' debugImplementation 'com.facebook.soloader:soloader:0.10.5' - releaseImplementation 'com.facebook.flipper:flipper-noop:0.256.0' + releaseImplementation 'com.facebook.flipper:flipper-noop:0.257.0' } ``` diff --git a/docs/getting-started/react-native-ios.mdx b/docs/getting-started/react-native-ios.mdx index ee062efbc1c..5bc7f3b0726 100644 --- a/docs/getting-started/react-native-ios.mdx +++ b/docs/getting-started/react-native-ios.mdx @@ -51,7 +51,7 @@ Add all of the code below to your `ios/Podfile`: platform :ios, '9.0' def flipper_pods() - flipperkit_version = '0.256.0' # should match the version of your Flipper client app + flipperkit_version = '0.257.0' # should match the version of your Flipper client app pod 'FlipperKit', '~>' + flipperkit_version, :configuration => 'Debug' pod 'FlipperKit/FlipperKitLayoutPlugin', '~>' + flipperkit_version, :configuration => 'Debug' pod 'FlipperKit/SKIOSNetworkPlugin', '~>' + flipperkit_version, :configuration => 'Debug' diff --git a/docs/getting-started/react-native.mdx b/docs/getting-started/react-native.mdx index d11383fbf25..2ece2118838 100644 --- a/docs/getting-started/react-native.mdx +++ b/docs/getting-started/react-native.mdx @@ -34,7 +34,7 @@ Latest version of Flipper requires react-native 0.69+! If you use react-native < Android: -1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.256.0`. +1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.257.0`. 2. Run `./gradlew clean` in the `android` directory. iOS: @@ -44,7 +44,7 @@ react-native version => 0.69.0 2. Run `pod install --repo-update` in the `ios` directory. react-native version < 0.69.0 -1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.256.0' })`. +1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.257.0' })`. 2. Run `pod install --repo-update` in the `ios` directory. ## Manual Setup diff --git a/gradle.properties b/gradle.properties index 1f380bf966d..613e90341da 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. # POM publishing constants -VERSION_NAME=0.256.1-SNAPSHOT +VERSION_NAME=0.257.0 GROUP=com.facebook.flipper SONATYPE_STAGING_PROFILE=comfacebook POM_URL=https://github.com/facebook/flipper diff --git a/js/js-flipper/package.json b/js/js-flipper/package.json index f7dc1997074..f1ee533711c 100644 --- a/js/js-flipper/package.json +++ b/js/js-flipper/package.json @@ -1,7 +1,7 @@ { "name": "js-flipper", "title": "JS Flipper Bindings for Web-Socket based clients", - "version": "0.256.0", + "version": "0.257.0", "main": "lib/index.js", "browser": { "os": false diff --git a/react-native/react-native-flipper/package.json b/react-native/react-native-flipper/package.json index 9f43cdc3359..1874bce6dee 100644 --- a/react-native/react-native-flipper/package.json +++ b/react-native/react-native-flipper/package.json @@ -1,7 +1,7 @@ { "name": "react-native-flipper", "title": "React Native Flipper Bindings", - "version": "0.256.0", + "version": "0.257.0", "description": "Flipper bindings for React Native", "main": "index.js", "types": "index.d.ts", From ba2b2761581567710e8d6ad33e1af9179951ef6e Mon Sep 17 00:00:00 2001 From: generatedunixname89002005306973 <generatedunixname89002005306973@meta.com> Date: Wed, 10 Jul 2024 05:57:08 -0700 Subject: [PATCH 91/92] Flipper Snapshot Bump: v0.257.1-SNAPSHOT Summary: Releasing snapshot version 0.257.1-SNAPSHOT Reviewed By: antonk52 Differential Revision: D59577588 fbshipit-source-id: b50dff88b85e518997e34a312969f4f67157239a --- docs/getting-started/android-native.mdx | 4 ++-- gradle.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/android-native.mdx b/docs/getting-started/android-native.mdx index 3794ecd3722..22acd81b231 100644 --- a/docs/getting-started/android-native.mdx +++ b/docs/getting-started/android-native.mdx @@ -124,10 +124,10 @@ repositories { } dependencies { - debugImplementation 'com.facebook.flipper:flipper:0.256.1-SNAPSHOT' + debugImplementation 'com.facebook.flipper:flipper:0.257.1-SNAPSHOT' debugImplementation 'com.facebook.soloader:soloader:0.10.5' - releaseImplementation 'com.facebook.flipper:flipper-noop:0.256.1-SNAPSHOT' + releaseImplementation 'com.facebook.flipper:flipper-noop:0.257.1-SNAPSHOT' } ``` diff --git a/gradle.properties b/gradle.properties index 613e90341da..c3a40ea144f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. # POM publishing constants -VERSION_NAME=0.257.0 +VERSION_NAME=0.257.1-SNAPSHOT GROUP=com.facebook.flipper SONATYPE_STAGING_PROFILE=comfacebook POM_URL=https://github.com/facebook/flipper From 954f58745a7c93786cf0c247cc2e0af0f66e3885 Mon Sep 17 00:00:00 2001 From: Pascal Hartig <realpassy@meta.com> Date: Wed, 10 Jul 2024 14:30:19 +0100 Subject: [PATCH 92/92] [android] Bump Litho dependency to 0.50.0 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index c3a40ea144f..477dcc53f43 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ POM_DEVELOPER_ID=facebook POM_DEVELOPER_NAME=facebook POM_ISSUES_URL=https://github.com/facebook/flipper/issues/ # Shared version numbers -LITHO_VERSION=0.49.0 +LITHO_VERSION=0.50.0 FRESCO_VERSION=3.1.3 ANDROIDX_VERSION=1.3.0 ANDROIDX_ACTIVITY_VERSION=1.8.2