From 0a79f6e2d0fef9ebf547f95c04d9862ebd328072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nishan=20=28o=5E=E2=96=BD=5Eo=29?= Date: Fri, 6 Mar 2026 16:33:46 +0530 Subject: [PATCH 1/7] [ui][android] - RNHostView throws "The specified child already has a parent" (#43691) # Why RNHostView should not call `super.addView` in `addView` since it renders the child using `AndroidView` compose view. This can lead to below error in some cases. e.g. RN mounts the child first and then compose tree renders and tries to attach child to `AndroidView`. ```bash java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. at android.view.ViewGroup.addViewInner(ViewGroup.java:5286) at android.view.ViewGroup.addView(ViewGroup.java:5115) at android.view.ViewGroup.addView(ViewGroup.java:5055) at android.view.ViewGroup.addView(ViewGroup.java:5027) ``` # How Do not add view using view methods but put it in state and render it via compose. # Test Plan Tested examples in NCL. # Checklist - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [x] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [x] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- packages/expo-ui/CHANGELOG.md | 1 + .../main/java/expo/modules/ui/RNHostView.kt | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/expo-ui/CHANGELOG.md b/packages/expo-ui/CHANGELOG.md index d539724f53c441..a739e122b9ec77 100644 --- a/packages/expo-ui/CHANGELOG.md +++ b/packages/expo-ui/CHANGELOG.md @@ -12,6 +12,7 @@ ### 🐛 Bug fixes +- [Android] Fix `RNHostView` child parent related crash. ([#43691](https://github.com/expo/expo/pull/43691) by [@nishan](https://github.com/intergalacticspacehighway)) - [Android] Added `RNHostView` to improve RN component layout inside Compose views. ([#43495](https://github.com/expo/expo/pull/43495) by [@nishan](https://github.com/intergalacticspacehighway)) - [Android] Fix `ContextMenu` not expanding when triggered by `IconButton`. ([#43592](https://github.com/expo/expo/pull/43592) by [@nishan](https://github.com/intergalacticspacehighway)) - [android] fix modifiers export ([#43639](https://github.com/expo/expo/pull/43639) by [@Ubax](https://github.com/Ubax)) diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/RNHostView.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/RNHostView.kt index 964e5dd951f565..3bab488aaf8aa2 100644 --- a/packages/expo-ui/android/src/main/java/expo/modules/ui/RNHostView.kt +++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/RNHostView.kt @@ -31,18 +31,29 @@ internal class RNHostView(context: Context, appContext: AppContext) : ExpoComposeView(context, appContext) { override val props = RNHostViewProps() - private var childView: View? = null + private val childViewState = mutableStateOf(null) override fun addView(child: View, index: Int, params: ViewGroup.LayoutParams) { - childView = child - super.addView(child, index, params) + childViewState.value = child + } + + override fun removeView(view: View) { + if (view == childViewState.value) { + childViewState.value = null + } else { + super.removeView(view) + } + } + + override fun removeViewAt(index: Int) { + childViewState.value = null } @Composable override fun ComposableScope.Content() { val matchContents = props.matchContents.value ?: false - childView?.let { view -> + childViewState.value?.let { view -> if (matchContents) { AndroidView( factory = { view }, From 8f4ffeed1353050d14b80b67eb49b3c010803e37 Mon Sep 17 00:00:00 2001 From: Patryk Mleczek <67064618+pmleczek@users.noreply.github.com> Date: Fri, 6 Mar 2026 13:11:50 +0100 Subject: [PATCH 2/7] [brownfield][android] set react native and hermes versions for all published artfacts (#43693) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Why As it has been reported in https://github.com/expo/expo/issues/43485 only the brownfield library has explicit versions of React Native and Hermes set # How - Extended setting RN and Hermes versions to all projects - Added stripping "old" Hermes (`com.facebook.react:hermes-android`) from POM - currently it's only stripped from module - Enabled caching of resolved RN and Hermes versions, so that we don't call `node ...` everytime we need them - Cleaned up the code (by removing passing versions) - Removed flaky logic for detecting brownfield project # Test Plan Created extended version of script from the issue, which validates both POM and module: https://gist.github.com/pmleczek/685261570033c3eeef804416056b927d All versions are correctly set: ```sh ──────────────────────────────────────────────────────────────────────── com.swmansion.reanimated 4.2.2 ──────────────────────────────────────────────────────────────────────── react-android pom: ✔ 0.84.0 .module: ✔ 0.84.0 hermes-android pom: ✗ .module: ✗ ──────────────────────────────────────────────────────────────────────── com.swmansion.worklets 0.7.4 ──────────────────────────────────────────────────────────────────────── react-android pom: ✔ 0.84.0 .module: ✔ 0.84.0 hermes-android pom: ✗ .module: ✗ ──────────────────────────────────────────────────────────────────────── expo.modules.brownfield 55.0.11 ──────────────────────────────────────────────────────────────────────── react-android pom: ✔ 0.84.0 .module: ✔ 0.84.0 hermes-android pom: ✗ .module: ✗ ──────────────────────────────────────────────────────────────────────── expo.modules.image 55.0.5 ──────────────────────────────────────────────────────────────────────── react-android pom: ✔ 0.84.0 .module: ✔ 0.84.0 hermes-android pom: ✗ .module: ✗ ──────────────────────────────────────────────────────────────────────── brownfield 1.0.0 ──────────────────────────────────────────────────────────────────────── react-android pom: ✔ 0.84.0 .module: ✔ 0.84.0 hermes-android pom: ✔ 250829098.0.7 .module: ✔ 250829098.0.7 ──────────────────────────────────────────────────────────────────────── expo.core 55.0.2 ──────────────────────────────────────────────────────────────────────── react-android pom: ✔ 0.84.0 .module: ✔ 0.84.0 hermes-android pom: ✗ .module: ✗ ──────────────────────────────────────────────────────────────────────── expo.interfaces.devmenu 55.0.1 ──────────────────────────────────────────────────────────────────────── react-android pom: ✔ 0.84.0 .module: ✔ 0.84.0 hermes-android pom: ✗ .module: ✗ ... ``` Also verified that app consuming the artifacts compiles and works # Checklist - [X] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [X] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). --- packages/expo-brownfield/CHANGELOG.md | 1 + .../kotlin/expo/modules/plugin/extensions.kt | 13 +-- .../kotlin/expo/modules/plugin/publishing.kt | 28 ++---- .../main/kotlin/expo/modules/plugin/utils.kt | 85 ++++++++++++++----- 4 files changed, 79 insertions(+), 48 deletions(-) diff --git a/packages/expo-brownfield/CHANGELOG.md b/packages/expo-brownfield/CHANGELOG.md index a554140c9577b6..331a322ca34954 100644 --- a/packages/expo-brownfield/CHANGELOG.md +++ b/packages/expo-brownfield/CHANGELOG.md @@ -17,6 +17,7 @@ ### 💡 Others - [test] add maestro e2e tests for dev menu ([#43421](https://github.com/expo/expo/pull/43421) by [@pmleczek](https://github.com/pmleczek)) +- [android] set react native version for all published artfacts ([#43693](https://github.com/expo/expo/pull/43693) by [@pmleczek](https://github.com/pmleczek)) ## 55.0.11 — 2026-02-25 diff --git a/packages/expo-brownfield/gradle-plugins/publish/src/main/kotlin/expo/modules/plugin/extensions.kt b/packages/expo-brownfield/gradle-plugins/publish/src/main/kotlin/expo/modules/plugin/extensions.kt index 86e87d12938c52..d5806e1333e0c8 100644 --- a/packages/expo-brownfield/gradle-plugins/publish/src/main/kotlin/expo/modules/plugin/extensions.kt +++ b/packages/expo-brownfield/gradle-plugins/publish/src/main/kotlin/expo/modules/plugin/extensions.kt @@ -179,11 +179,14 @@ internal fun PublishingExtension.createPublication( from: String, project: Project, libraryExtension: LibraryExtension, - rnVersion: String?, - hermesVersion: String?, ) { + val rnVersion = getReactNativeVersion(project) + val hermesVersion = getHermesVersion(project) + + val isBrownfieldProject = project.plugins.hasPlugin("expo-brownfield-setup") + val _artifactId = - if (rnVersion != null) { + if (isBrownfieldProject) { project.name } else { requireNotNull(libraryExtension.namespace) @@ -198,9 +201,7 @@ internal fun PublishingExtension.createPublication( pom.withXml { xml -> removeReactNativeDependencyPom(xml) - if (rnVersion != null && hermesVersion != null) { - setReactNativeVersionPom(xml, rnVersion, hermesVersion) - } + setReactNativeVersionPom(xml, rnVersion, hermesVersion) } } } diff --git a/packages/expo-brownfield/gradle-plugins/publish/src/main/kotlin/expo/modules/plugin/publishing.kt b/packages/expo-brownfield/gradle-plugins/publish/src/main/kotlin/expo/modules/plugin/publishing.kt index ae2d27c68cfb77..bc4e2eaf812e48 100644 --- a/packages/expo-brownfield/gradle-plugins/publish/src/main/kotlin/expo/modules/plugin/publishing.kt +++ b/packages/expo-brownfield/gradle-plugins/publish/src/main/kotlin/expo/modules/plugin/publishing.kt @@ -30,24 +30,11 @@ internal fun setupPublishing(project: Project) { return@afterEvaluate } - val rnVersion = - if (project.name == configExtension.libraryName.get()) { - getReactNativeVersion(project) - } else { - null - } - val hermesVersion = - if (project.name == configExtension.libraryName.get()) { - getHermesVersion(project) - } else { - null - } - variants.forEach { variant -> - publicationExtension.createPublication(variant, project, libraryExtension, rnVersion, hermesVersion) + publicationExtension.createPublication(variant, project, libraryExtension) } - createModuleRelatedTasks(project, rnVersion, hermesVersion) + createModuleRelatedTasks(project) setupRepositories(publicationExtension, project, configExtension) } } @@ -89,13 +76,11 @@ internal fun setupRepositories( * @param project The project to remove react-native dependency from. * @param isBrownfieldProject Whether the project is the brownfield project. */ -internal fun createModuleRelatedTasks(project: Project, rnVersion: String?, hermesVersion: String?) { +internal fun createModuleRelatedTasks(project: Project) { val variants = listOf("brownfieldDebug", "brownfieldRelease", "brownfieldAll") variants.forEach { variant -> createRemoveReactNativeDependencyModuleTask(project, variant) - if (rnVersion != null && hermesVersion != null) { - createSetReactNativeVersionModuleTask(project, variant, rnVersion, hermesVersion) - } + createSetReactNativeVersionModuleTask(project, variant) } } @@ -147,9 +132,10 @@ internal fun createRemoveReactNativeDependencyModuleTask(project: Project, varia internal fun createSetReactNativeVersionModuleTask( project: Project, variant: String, - rnVersion: String, - hermesVersion: String, ) { + val rnVersion = getReactNativeVersion(project) + val hermesVersion = getHermesVersion(project) + val setVersionTask = project.tasks.register("setRNDependencyVersionInModuleFile$variant") { task -> task.doLast { diff --git a/packages/expo-brownfield/gradle-plugins/publish/src/main/kotlin/expo/modules/plugin/utils.kt b/packages/expo-brownfield/gradle-plugins/publish/src/main/kotlin/expo/modules/plugin/utils.kt index f0c356a343aff3..afa275d0ac3ac6 100644 --- a/packages/expo-brownfield/gradle-plugins/publish/src/main/kotlin/expo/modules/plugin/utils.kt +++ b/packages/expo-brownfield/gradle-plugins/publish/src/main/kotlin/expo/modules/plugin/utils.kt @@ -8,8 +8,12 @@ import java.io.File import org.gradle.api.Project import org.gradle.api.XmlProvider import org.gradle.api.publish.PublishingExtension +import org.gradle.api.plugins.ExtraPropertiesExtension import org.json.JSONObject +private const val EXPO_BROWNFIELD_RN_VERSION_KEY = "expoBrownfieldRnVersion" +private const val EXPO_BROWNFIELD_HERMES_VERSION_KEY = "expoBrownfieldHermesVersion" + /** * Find the app project in the root project. * @@ -105,13 +109,31 @@ internal fun getConfigExtension(project: Project): ExpoPublishExtension { } /** - * Get the React Native version for the project. + * Return cached React Native version for the project. + * + * @param project The project to get the React Native version for. + * @return The cached React Native version for the project. + */ +internal fun getReactNativeVersion(project: Project): String { + val rootProject = project.rootProject + val ext = rootProject.extensions.getByType(ExtraPropertiesExtension::class.java) + if (!ext.has(EXPO_BROWNFIELD_RN_VERSION_KEY)) { + val version = getReactNativeVersionInternal(project) + ext.set(EXPO_BROWNFIELD_RN_VERSION_KEY, version) + return version + } + + return ext.get(EXPO_BROWNFIELD_RN_VERSION_KEY) as String +} + +/** + * Get the React Native version from the project. * * @param project The project to get the React Native version for. * @return The React Native version for the project. * @throws IllegalStateException if the React Native version cannot be inferred. */ -internal fun getReactNativeVersion(project: Project): String { +internal fun getReactNativeVersionInternal(project: Project): String { return try { val process = ProcessBuilder("node", "--print", "require('react-native/package.json').version") @@ -134,30 +156,48 @@ internal fun getReactNativeVersion(project: Project): String { } } - /** - * Get the Hermes version for the project. - * - * @param project The project to get the Hermes version for. - * @return The Hermes version for the project. - * @throws IllegalStateException if the Hermes version cannot be inferred. - */ - internal fun getHermesVersion(project: Project): String { - val process = - ProcessBuilder("node", "--print", "require('hermes-compiler/package.json').version") - .directory(project.rootProject.projectDir) - .redirectErrorStream(true) - .start() +/** + * Return cached Hermes version for the project. + * + * @param project The project to get the Hermes version for. + * @return The cached Hermes version for the project. + */ +internal fun getHermesVersion(project: Project): String { + val rootProject = project.rootProject + val ext = rootProject.extensions.getByType(ExtraPropertiesExtension::class.java) + if (!ext.has(EXPO_BROWNFIELD_HERMES_VERSION_KEY)) { + val version = getHermesVersionInternal(project) + ext.set(EXPO_BROWNFIELD_HERMES_VERSION_KEY, version) + return version + } - val version = process.inputStream.bufferedReader().readText().trim() - process.waitFor() + return ext.get(EXPO_BROWNFIELD_HERMES_VERSION_KEY) as String +} - if (process.exitValue() == 0 && version.isNotEmpty()) { - return version - } +/** + * Get the Hermes version for the project. + * + * @param project The project to get the Hermes version for. + * @return The Hermes version for the project. + * @throws IllegalStateException if the Hermes version cannot be inferred. + */ +internal fun getHermesVersionInternal(project: Project): String { + val process = + ProcessBuilder("node", "--print", "require('hermes-compiler/package.json').version") + .directory(project.rootProject.projectDir) + .redirectErrorStream(true) + .start() + + val version = process.inputStream.bufferedReader().readText().trim() + process.waitFor() - throw IllegalStateException("Failed to infer Hermes version via Node") + if (process.exitValue() == 0 && version.isNotEmpty()) { + return version } + throw IllegalStateException("Failed to infer Hermes version via Node") +} + /** * Get the React Native version from the package.json file. * @@ -219,6 +259,9 @@ internal fun removeReactNativeDependencyPom(xml: XmlProvider) { if (dependency.groupId() == "com.facebook.react" && dependency.artifactId() == "react-native") { toRemove.add(dependency) } + if (dependency.groupId() == "com.facebook.react" && dependency.artifactId() == "hermes-android") { + toRemove.add(dependency) + } } val dependenciesNode = xml.dependenciesNode() From 6727f7356c1db5e52f09b31f870fb93445b3d655 Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Fri, 6 Mar 2026 12:46:03 +0000 Subject: [PATCH 3/7] fix(autolinking): Stop recursive dependency traversal at depth limit (#43636) # Why Previously, the recursive dependency traversal doesn't stop at the depth limit. Instead, it still uses that depth's information and prepares a list of dependencies. However, it skips the (more expensive `package.json` read step). This preserved more information and re-used results at the maximum depth. However, this doesn't provide the version, which is used and exposed via the `resolve` command. This can have an impact on `@expo/fingerprint`. Additionally, while it's not known which cases can cause non-deterministic sorting, we can make non-deterministic outputs more unlikely by: - Sorting on `originPath` itself when depth of the dependency and depth of the `originPath` match - Load the `version` of duplicates regardless of `path` already having been traversed (which is a duplicate `loadPackageJson` call, but the memoisation should help here) # How While I didn't anticipate that `MAX_DEPTH = 8` would ever be exceeded by native modules, there's two things we can cheaply do: - Stop traversal at depth limit immediately to prevent creating dependencies with unresolved `version: ''` entries - Increase depth limit by +2 (+1 compensates for the traversal stop, another +1 since hitting this bug means `MAX_DEPTH = 8` may have been insufficient in some rare cases) Additionally, there seem to be a reported case in #43517 of non-deterministic outputs in edge cases with an unknown cause: - We can further sort on `orginPath` to make this unlikely - We can always load the `version` to make sure it's never missing I've validated that for `router-e2e` this doesn't meaningfully increase autolinking time by more than 5%. # Test Plan - Check the added unit test from #43517. Once the depth limit is reached, we ignore further dependencies in the dependency tree, instead of adding one more layer of dependencies with `version: ''` - Manually run resolution in a sample app (e.g. `router-e2e`) against `main` with hyperfine to quickly check the performance impact # Checklist - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --------- Co-authored-by: Dawid van der Hoven Co-authored-by: Kudo Chien --- .../expo-modules-autolinking/CHANGELOG.md | 3 ++ .../build/dependencies/resolution.js | 22 ++++----- .../build/dependencies/resolution.js.map | 2 +- .../build/dependencies/utils.js | 8 ++++ .../build/dependencies/utils.js.map | 2 +- .../dependencies/__tests__/resolution-test.ts | 46 +++++++++++++------ .../src/dependencies/resolution.ts | 32 ++++++------- .../src/dependencies/utils.ts | 6 +++ 8 files changed, 73 insertions(+), 48 deletions(-) diff --git a/packages/expo-modules-autolinking/CHANGELOG.md b/packages/expo-modules-autolinking/CHANGELOG.md index 9f5b667a20a8d8..cb67b3526464dc 100644 --- a/packages/expo-modules-autolinking/CHANGELOG.md +++ b/packages/expo-modules-autolinking/CHANGELOG.md @@ -8,6 +8,9 @@ ### 🐛 Bug fixes +- Stop dependency resolution at depth limit and increase max depth limit to 9 ([#43636](https://github.com/expo/expo/pull/43636) by [@kitten](https://github.com/kitten)) +- Sort on unresolved path and load `version` for duplicate dependencies ([#43636](https://github.com/expo/expo/pull/43636) by [@kitten](https://github.com/kitten)) + ### 💡 Others ## 55.0.8 — 2026-02-25 diff --git a/packages/expo-modules-autolinking/build/dependencies/resolution.js b/packages/expo-modules-autolinking/build/dependencies/resolution.js index 6394337e2b340f..f6a90623473431 100644 --- a/packages/expo-modules-autolinking/build/dependencies/resolution.js +++ b/packages/expo-modules-autolinking/build/dependencies/resolution.js @@ -10,7 +10,7 @@ const concurrency_1 = require("../concurrency"); const utils_2 = require("../utils"); // NOTE(@kitten): There's no need to search very deep for modules // We don't expect native modules to be excessively nested in the dependency tree -const MAX_DEPTH = 8; +const MAX_DEPTH = 9; const createNodeModulePathsCreator = () => { const _nodeModulePathCache = new Map(); return async function getNodeModulePaths(packagePath) { @@ -88,14 +88,10 @@ async function scanDependenciesRecursively(rawPath, { shouldIncludeDependency = const maxDepth = limitDepth != null ? limitDepth : MAX_DEPTH; const recurse = async (resolution, depth = 0) => { const searchResults = Object.create(null); - if (_visitedPackagePaths.has(resolution.path)) { - return searchResults; - } - else { - _visitedPackagePaths.add(resolution.path); - } + const hasVisitedPath = _visitedPackagePaths.has(resolution.path); + _visitedPackagePaths.add(resolution.path); const [nodeModulePaths, packageJson] = await Promise.all([ - getNodeModulePaths(resolution.path), + !hasVisitedPath ? getNodeModulePaths(resolution.path) : [], (0, utils_2.loadPackageJson)((0, utils_2.fastJoin)(resolution.path, 'package.json')), ]); if (!packageJson) { @@ -104,11 +100,11 @@ async function scanDependenciesRecursively(rawPath, { shouldIncludeDependency = else { resolution.version = packageJson.version || ''; } - const modules = await resolveDependencies(packageJson, nodeModulePaths, depth, shouldIncludeDependency); - for (let idx = 0; idx < modules.length; idx++) { - searchResults[modules[idx].name] = modules[idx]; - } - if (depth + 1 < maxDepth) { + if (!hasVisitedPath && depth + 1 < maxDepth) { + const modules = await resolveDependencies(packageJson, nodeModulePaths, depth, shouldIncludeDependency); + for (let idx = 0; idx < modules.length; idx++) { + searchResults[modules[idx].name] = modules[idx]; + } const childResults = await (0, concurrency_1.taskAll)(modules, (resolution) => recurse(resolution, depth + 1)); return (0, utils_1.mergeResolutionResults)(childResults, searchResults); } diff --git a/packages/expo-modules-autolinking/build/dependencies/resolution.js.map b/packages/expo-modules-autolinking/build/dependencies/resolution.js.map index 143e927d87fbd3..15f6162e27eb35 100644 --- a/packages/expo-modules-autolinking/build/dependencies/resolution.js.map +++ b/packages/expo-modules-autolinking/build/dependencies/resolution.js.map @@ -1 +1 @@ -{"version":3,"file":"resolution.js","sourceRoot":"","sources":["../../src/dependencies/resolution.ts"],"names":[],"mappings":";;;;;AA6GA,kEA8DC;AA3KD,8DAAiC;AAOjC,mCAAiF;AACjF,gDAAyC;AACzC,oCAAsF;AAMtF,iEAAiE;AACjE,iFAAiF;AACjF,MAAM,SAAS,GAAG,CAAC,CAAC;AAEpB,MAAM,4BAA4B,GAAG,GAAG,EAAE;IACxC,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC9D,OAAO,KAAK,UAAU,kBAAkB,CAAC,WAAmB;QAC1D,MAAM,WAAW,GAAa,EAAE,CAAC;QACjC,MAAM,eAAe,GAAG,qBAAM,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;QAC7D,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;YACtD,MAAM,cAAc,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;YAC5C,IAAI,MAAM,GAAG,oBAAoB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YACtD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,MAAM,GAAG,MAAM,IAAA,qBAAa,EAAC,cAAc,CAAC,CAAC;gBAC7C,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC;oBACd,oBAAoB,CAAC,GAAG,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;gBACnD,CAAC;YACH,CAAC;YACD,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;gBACnB,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QACD,OAAO,WAAW,CAAC;IACrB,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,KAAK,UAAU,mBAAmB,CAChC,WAAwB,EACxB,eAAkC,EAClC,KAAa,EACb,uBAA4D;IAE5D,MAAM,YAAY,GAA2B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACjE,IAAI,WAAW,CAAC,YAAY,IAAI,IAAI,IAAI,OAAO,WAAW,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;QACrF,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,WAAW,CAAC,YAAY,CAAC,CAAC;IACxD,CAAC;IACD,0EAA0E;IAC1E,IACE,KAAK,KAAK,CAAC;QACX,WAAW,CAAC,eAAe,IAAI,IAAI;QACnC,OAAO,WAAW,CAAC,eAAe,KAAK,QAAQ,EAC/C,CAAC;QACD,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,WAAW,CAAC,gBAAgB,IAAI,IAAI,IAAI,OAAO,WAAW,CAAC,gBAAgB,KAAK,QAAQ,EAAE,CAAC;QAC7F,MAAM,oBAAoB,GACxB,WAAW,CAAC,oBAAoB,IAAI,IAAI;YACxC,OAAO,WAAW,CAAC,oBAAoB,KAAK,QAAQ;YAClD,CAAC,CAAE,WAAW,CAAC,oBAAgD;YAC/D,CAAC,CAAC,SAAS,CAAC;QAChB,KAAK,MAAM,cAAc,IAAI,WAAW,CAAC,gBAAgB,EAAE,CAAC;YAC1D,iGAAiG;YACjG,qGAAqG;YACrG,0CAA0C;YAC1C,IAAI,CAAC,4BAA4B,CAAC,oBAAoB,EAAE,cAAc,CAAC,EAAE,CAAC;gBACxE,YAAY,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC;YACpC,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,iBAAiB,GAAG,KAAK,EAC7B,cAAsB,EACgB,EAAE;QACxC,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;YACtD,MAAM,UAAU,GAAG,IAAA,gBAAQ,EAAC,eAAe,CAAC,GAAG,CAAC,EAAE,cAAc,CAAC,CAAC;YAClE,MAAM,cAAc,GAAG,MAAM,IAAA,qBAAa,EAAC,UAAU,CAAC,CAAC;YACvD,IAAI,cAAc,IAAI,IAAI,EAAE,CAAC;gBAC3B,OAAO;oBACL,MAAM,yDAAiD;oBACvD,IAAI,EAAE,cAAc;oBACpB,OAAO,EAAE,EAAE;oBACX,IAAI,EAAE,cAAc;oBACpB,UAAU;oBACV,UAAU,EAAE,IAAI;oBAChB,KAAK;iBACN,CAAC;YACJ,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,MAAM,IAAA,qBAAO,EAC3B,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,uBAAuB,CAAC,cAAc,CAAC,CAAC,EAC7F,CAAC,cAAc,EAAE,EAAE,CAAC,iBAAiB,CAAC,cAAc,CAAC,CACtD,CAAC;IAEF,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,IAAI,IAAI,CAAC,CAAC;AAC5D,CAAC;AAOM,KAAK,UAAU,2BAA2B,CAC/C,OAAe,EACf,EAAE,uBAAuB,GAAG,sCAA8B,EAAE,UAAU,KAAwB,EAAE;IAEhG,MAAM,QAAQ,GAAG,MAAM,IAAA,qBAAa,EAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAE,CAAC;IACvC,MAAM,kBAAkB,GAAG,4BAA4B,EAAE,CAAC;IAC1D,MAAM,QAAQ,GAAG,UAAU,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAE7D,MAAM,OAAO,GAAG,KAAK,EACnB,UAAgC,EAChC,KAAK,GAAG,CAAC,EACkB,EAAE;QAC7B,MAAM,aAAa,GAAqB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC5D,IAAI,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9C,OAAO,aAAa,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC5C,CAAC;QACD,MAAM,CAAC,eAAe,EAAE,WAAW,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACvD,kBAAkB,CAAC,UAAU,CAAC,IAAI,CAAC;YACnC,IAAA,uBAAe,EAAC,IAAA,gBAAQ,EAAC,UAAU,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;SAC3D,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,aAAa,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO,IAAI,EAAE,CAAC;QACjD,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,mBAAmB,CACvC,WAAW,EACX,eAAe,EACf,KAAK,EACL,uBAAuB,CACxB,CAAC;QACF,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;YAC9C,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAClD,CAAC;QAED,IAAI,KAAK,GAAG,CAAC,GAAG,QAAQ,EAAE,CAAC;YACzB,MAAM,YAAY,GAAG,MAAM,IAAA,qBAAO,EAAC,OAAO,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;YAC5F,OAAO,IAAA,8BAAsB,EAAC,YAAY,EAAE,aAAa,CAAC,CAAC;QAC7D,CAAC;aAAM,CAAC;YACN,OAAO,aAAa,CAAC;QACvB,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC;QAClC,MAAM,yDAAiD;QACvD,IAAI,EAAE,EAAE;QACR,OAAO,EAAE,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,OAAO;QACnB,UAAU,EAAE,IAAI;QAChB,KAAK,EAAE,CAAC,CAAC;KACV,CAAC,CAAC;IAEH,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,MAAM,4BAA4B,GAAG,CACnC,oBAAyD,EACzD,WAAmB,EACnB,EAAE;IACF,OAAO,CACL,oBAAoB;QACpB,oBAAoB,CAAC,WAAW,CAAC,IAAI,IAAI;QACzC,OAAO,oBAAoB,CAAC,WAAW,CAAC,KAAK,QAAQ;QACrD,UAAU,IAAI,oBAAoB,CAAC,WAAW,CAAC;QAC/C,CAAC,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC,QAAQ,CAC7C,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import Module from 'node:module';\n\nimport {\n type ResolutionResult,\n type DependencyResolution,\n DependencyResolutionSource,\n} from './types';\nimport { defaultShouldIncludeDependency, mergeResolutionResults } from './utils';\nimport { taskAll } from '../concurrency';\nimport { type PackageJson, loadPackageJson, maybeRealpath, fastJoin } from '../utils';\n\ndeclare module 'node:module' {\n export function _nodeModulePaths(base: string): readonly string[];\n}\n\n// NOTE(@kitten): There's no need to search very deep for modules\n// We don't expect native modules to be excessively nested in the dependency tree\nconst MAX_DEPTH = 8;\n\nconst createNodeModulePathsCreator = () => {\n const _nodeModulePathCache = new Map();\n return async function getNodeModulePaths(packagePath: string) {\n const outputPaths: string[] = [];\n const nodeModulePaths = Module._nodeModulePaths(packagePath);\n for (let idx = 0; idx < nodeModulePaths.length; idx++) {\n const nodeModulePath = nodeModulePaths[idx];\n let target = _nodeModulePathCache.get(nodeModulePath);\n if (target === undefined) {\n target = await maybeRealpath(nodeModulePath);\n if (idx !== 0) {\n _nodeModulePathCache.set(nodeModulePath, target);\n }\n }\n if (target != null) {\n outputPaths.push(target);\n }\n }\n return outputPaths;\n };\n};\n\nasync function resolveDependencies(\n packageJson: PackageJson,\n nodeModulePaths: readonly string[],\n depth: number,\n shouldIncludeDependency: (dependencyName: string) => boolean\n): Promise {\n const dependencies: Record = Object.create(null);\n if (packageJson.dependencies != null && typeof packageJson.dependencies === 'object') {\n Object.assign(dependencies, packageJson.dependencies);\n }\n // NOTE(@kitten): Also traverse devDependencies for top-level package.json\n if (\n depth === 0 &&\n packageJson.devDependencies != null &&\n typeof packageJson.devDependencies === 'object'\n ) {\n Object.assign(dependencies, packageJson.devDependencies);\n }\n if (packageJson.peerDependencies != null && typeof packageJson.peerDependencies === 'object') {\n const peerDependenciesMeta =\n packageJson.peerDependenciesMeta != null &&\n typeof packageJson.peerDependenciesMeta === 'object'\n ? (packageJson.peerDependenciesMeta as Record)\n : undefined;\n for (const dependencyName in packageJson.peerDependencies) {\n // NOTE(@kitten): We only check peer dependencies because some package managers auto-install them\n // which would mean they'd have no reference in any dependencies. However, optional peer dependencies\n // don't auto-install and we can skip them\n if (!isOptionalPeerDependencyMeta(peerDependenciesMeta, dependencyName)) {\n dependencies[dependencyName] = '';\n }\n }\n }\n\n const resolveDependency = async (\n dependencyName: string\n ): Promise => {\n for (let idx = 0; idx < nodeModulePaths.length; idx++) {\n const originPath = fastJoin(nodeModulePaths[idx], dependencyName);\n const nodeModulePath = await maybeRealpath(originPath);\n if (nodeModulePath != null) {\n return {\n source: DependencyResolutionSource.RECURSIVE_RESOLUTION,\n name: dependencyName,\n version: '',\n path: nodeModulePath,\n originPath,\n duplicates: null,\n depth,\n };\n }\n }\n return null;\n };\n\n const modules = await taskAll(\n Object.keys(dependencies).filter((dependencyName) => shouldIncludeDependency(dependencyName)),\n (dependencyName) => resolveDependency(dependencyName)\n );\n\n return modules.filter((resolution) => resolution != null);\n}\n\ninterface ResolutionOptions {\n shouldIncludeDependency?(name: string): boolean;\n limitDepth?: number;\n}\n\nexport async function scanDependenciesRecursively(\n rawPath: string,\n { shouldIncludeDependency = defaultShouldIncludeDependency, limitDepth }: ResolutionOptions = {}\n): Promise {\n const rootPath = await maybeRealpath(rawPath);\n if (!rootPath) {\n return {};\n }\n\n const _visitedPackagePaths = new Set();\n const getNodeModulePaths = createNodeModulePathsCreator();\n const maxDepth = limitDepth != null ? limitDepth : MAX_DEPTH;\n\n const recurse = async (\n resolution: DependencyResolution,\n depth = 0\n ): Promise => {\n const searchResults: ResolutionResult = Object.create(null);\n if (_visitedPackagePaths.has(resolution.path)) {\n return searchResults;\n } else {\n _visitedPackagePaths.add(resolution.path);\n }\n const [nodeModulePaths, packageJson] = await Promise.all([\n getNodeModulePaths(resolution.path),\n loadPackageJson(fastJoin(resolution.path, 'package.json')),\n ]);\n if (!packageJson) {\n return searchResults;\n } else {\n resolution.version = packageJson.version || '';\n }\n\n const modules = await resolveDependencies(\n packageJson,\n nodeModulePaths,\n depth,\n shouldIncludeDependency\n );\n for (let idx = 0; idx < modules.length; idx++) {\n searchResults[modules[idx].name] = modules[idx];\n }\n\n if (depth + 1 < maxDepth) {\n const childResults = await taskAll(modules, (resolution) => recurse(resolution, depth + 1));\n return mergeResolutionResults(childResults, searchResults);\n } else {\n return searchResults;\n }\n };\n\n const searchResults = await recurse({\n source: DependencyResolutionSource.RECURSIVE_RESOLUTION,\n name: '',\n version: '',\n path: rootPath,\n originPath: rawPath,\n duplicates: null,\n depth: -1,\n });\n\n return searchResults;\n}\n\nconst isOptionalPeerDependencyMeta = (\n peerDependenciesMeta: Record | undefined,\n packageName: string\n) => {\n return (\n peerDependenciesMeta &&\n peerDependenciesMeta[packageName] != null &&\n typeof peerDependenciesMeta[packageName] === 'object' &&\n 'optional' in peerDependenciesMeta[packageName] &&\n !!peerDependenciesMeta[packageName].optional\n );\n};\n"]} \ No newline at end of file +{"version":3,"file":"resolution.js","sourceRoot":"","sources":["../../src/dependencies/resolution.ts"],"names":[],"mappings":";;;;;AA6GA,kEA0DC;AAvKD,8DAAiC;AAOjC,mCAAiF;AACjF,gDAAyC;AACzC,oCAAsF;AAMtF,iEAAiE;AACjE,iFAAiF;AACjF,MAAM,SAAS,GAAG,CAAC,CAAC;AAEpB,MAAM,4BAA4B,GAAG,GAAG,EAAE;IACxC,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC9D,OAAO,KAAK,UAAU,kBAAkB,CAAC,WAAmB;QAC1D,MAAM,WAAW,GAAa,EAAE,CAAC;QACjC,MAAM,eAAe,GAAG,qBAAM,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC;QAC7D,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;YACtD,MAAM,cAAc,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;YAC5C,IAAI,MAAM,GAAG,oBAAoB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YACtD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,MAAM,GAAG,MAAM,IAAA,qBAAa,EAAC,cAAc,CAAC,CAAC;gBAC7C,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC;oBACd,oBAAoB,CAAC,GAAG,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;gBACnD,CAAC;YACH,CAAC;YACD,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;gBACnB,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QACD,OAAO,WAAW,CAAC;IACrB,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,KAAK,UAAU,mBAAmB,CAChC,WAAwB,EACxB,eAAkC,EAClC,KAAa,EACb,uBAA4D;IAE5D,MAAM,YAAY,GAA2B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACjE,IAAI,WAAW,CAAC,YAAY,IAAI,IAAI,IAAI,OAAO,WAAW,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;QACrF,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,WAAW,CAAC,YAAY,CAAC,CAAC;IACxD,CAAC;IACD,0EAA0E;IAC1E,IACE,KAAK,KAAK,CAAC;QACX,WAAW,CAAC,eAAe,IAAI,IAAI;QACnC,OAAO,WAAW,CAAC,eAAe,KAAK,QAAQ,EAC/C,CAAC;QACD,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,WAAW,CAAC,gBAAgB,IAAI,IAAI,IAAI,OAAO,WAAW,CAAC,gBAAgB,KAAK,QAAQ,EAAE,CAAC;QAC7F,MAAM,oBAAoB,GACxB,WAAW,CAAC,oBAAoB,IAAI,IAAI;YACxC,OAAO,WAAW,CAAC,oBAAoB,KAAK,QAAQ;YAClD,CAAC,CAAE,WAAW,CAAC,oBAAgD;YAC/D,CAAC,CAAC,SAAS,CAAC;QAChB,KAAK,MAAM,cAAc,IAAI,WAAW,CAAC,gBAAgB,EAAE,CAAC;YAC1D,iGAAiG;YACjG,qGAAqG;YACrG,0CAA0C;YAC1C,IAAI,CAAC,4BAA4B,CAAC,oBAAoB,EAAE,cAAc,CAAC,EAAE,CAAC;gBACxE,YAAY,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC;YACpC,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,iBAAiB,GAAG,KAAK,EAC7B,cAAsB,EACgB,EAAE;QACxC,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;YACtD,MAAM,UAAU,GAAG,IAAA,gBAAQ,EAAC,eAAe,CAAC,GAAG,CAAC,EAAE,cAAc,CAAC,CAAC;YAClE,MAAM,cAAc,GAAG,MAAM,IAAA,qBAAa,EAAC,UAAU,CAAC,CAAC;YACvD,IAAI,cAAc,IAAI,IAAI,EAAE,CAAC;gBAC3B,OAAO;oBACL,MAAM,yDAAiD;oBACvD,IAAI,EAAE,cAAc;oBACpB,OAAO,EAAE,EAAE;oBACX,IAAI,EAAE,cAAc;oBACpB,UAAU;oBACV,UAAU,EAAE,IAAI;oBAChB,KAAK;iBACN,CAAC;YACJ,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,MAAM,IAAA,qBAAO,EAC3B,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,EAAE,CAAC,uBAAuB,CAAC,cAAc,CAAC,CAAC,EAC7F,CAAC,cAAc,EAAE,EAAE,CAAC,iBAAiB,CAAC,cAAc,CAAC,CACtD,CAAC;IAEF,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,IAAI,IAAI,CAAC,CAAC;AAC5D,CAAC;AAOM,KAAK,UAAU,2BAA2B,CAC/C,OAAe,EACf,EAAE,uBAAuB,GAAG,sCAA8B,EAAE,UAAU,KAAwB,EAAE;IAEhG,MAAM,QAAQ,GAAG,MAAM,IAAA,qBAAa,EAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAE,CAAC;IACvC,MAAM,kBAAkB,GAAG,4BAA4B,EAAE,CAAC;IAC1D,MAAM,QAAQ,GAAG,UAAU,IAAI,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAE7D,MAAM,OAAO,GAAG,KAAK,EACnB,UAAgC,EAChC,KAAK,GAAG,CAAC,EACkB,EAAE;QAC7B,MAAM,aAAa,GAAqB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC5D,MAAM,cAAc,GAAG,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACjE,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,eAAe,EAAE,WAAW,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACvD,CAAC,cAAc,CAAC,CAAC,CAAC,kBAAkB,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE;YAC1D,IAAA,uBAAe,EAAC,IAAA,gBAAQ,EAAC,UAAU,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC;SAC3D,CAAC,CAAC;QACH,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,OAAO,aAAa,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO,IAAI,EAAE,CAAC;QACjD,CAAC;QAED,IAAI,CAAC,cAAc,IAAI,KAAK,GAAG,CAAC,GAAG,QAAQ,EAAE,CAAC;YAC5C,MAAM,OAAO,GAAG,MAAM,mBAAmB,CACvC,WAAW,EACX,eAAe,EACf,KAAK,EACL,uBAAuB,CACxB,CAAC;YACF,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC;gBAC9C,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;YAClD,CAAC;YACD,MAAM,YAAY,GAAG,MAAM,IAAA,qBAAO,EAAC,OAAO,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;YAC5F,OAAO,IAAA,8BAAsB,EAAC,YAAY,EAAE,aAAa,CAAC,CAAC;QAC7D,CAAC;aAAM,CAAC;YACN,OAAO,aAAa,CAAC;QACvB,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC;QAClC,MAAM,yDAAiD;QACvD,IAAI,EAAE,EAAE;QACR,OAAO,EAAE,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,OAAO;QACnB,UAAU,EAAE,IAAI;QAChB,KAAK,EAAE,CAAC,CAAC;KACV,CAAC,CAAC;IAEH,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,MAAM,4BAA4B,GAAG,CACnC,oBAAyD,EACzD,WAAmB,EACnB,EAAE;IACF,OAAO,CACL,oBAAoB;QACpB,oBAAoB,CAAC,WAAW,CAAC,IAAI,IAAI;QACzC,OAAO,oBAAoB,CAAC,WAAW,CAAC,KAAK,QAAQ;QACrD,UAAU,IAAI,oBAAoB,CAAC,WAAW,CAAC;QAC/C,CAAC,CAAC,oBAAoB,CAAC,WAAW,CAAC,CAAC,QAAQ,CAC7C,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import Module from 'node:module';\n\nimport {\n type ResolutionResult,\n type DependencyResolution,\n DependencyResolutionSource,\n} from './types';\nimport { defaultShouldIncludeDependency, mergeResolutionResults } from './utils';\nimport { taskAll } from '../concurrency';\nimport { type PackageJson, loadPackageJson, maybeRealpath, fastJoin } from '../utils';\n\ndeclare module 'node:module' {\n export function _nodeModulePaths(base: string): readonly string[];\n}\n\n// NOTE(@kitten): There's no need to search very deep for modules\n// We don't expect native modules to be excessively nested in the dependency tree\nconst MAX_DEPTH = 9;\n\nconst createNodeModulePathsCreator = () => {\n const _nodeModulePathCache = new Map();\n return async function getNodeModulePaths(packagePath: string) {\n const outputPaths: string[] = [];\n const nodeModulePaths = Module._nodeModulePaths(packagePath);\n for (let idx = 0; idx < nodeModulePaths.length; idx++) {\n const nodeModulePath = nodeModulePaths[idx];\n let target = _nodeModulePathCache.get(nodeModulePath);\n if (target === undefined) {\n target = await maybeRealpath(nodeModulePath);\n if (idx !== 0) {\n _nodeModulePathCache.set(nodeModulePath, target);\n }\n }\n if (target != null) {\n outputPaths.push(target);\n }\n }\n return outputPaths;\n };\n};\n\nasync function resolveDependencies(\n packageJson: PackageJson,\n nodeModulePaths: readonly string[],\n depth: number,\n shouldIncludeDependency: (dependencyName: string) => boolean\n): Promise {\n const dependencies: Record = Object.create(null);\n if (packageJson.dependencies != null && typeof packageJson.dependencies === 'object') {\n Object.assign(dependencies, packageJson.dependencies);\n }\n // NOTE(@kitten): Also traverse devDependencies for top-level package.json\n if (\n depth === 0 &&\n packageJson.devDependencies != null &&\n typeof packageJson.devDependencies === 'object'\n ) {\n Object.assign(dependencies, packageJson.devDependencies);\n }\n if (packageJson.peerDependencies != null && typeof packageJson.peerDependencies === 'object') {\n const peerDependenciesMeta =\n packageJson.peerDependenciesMeta != null &&\n typeof packageJson.peerDependenciesMeta === 'object'\n ? (packageJson.peerDependenciesMeta as Record)\n : undefined;\n for (const dependencyName in packageJson.peerDependencies) {\n // NOTE(@kitten): We only check peer dependencies because some package managers auto-install them\n // which would mean they'd have no reference in any dependencies. However, optional peer dependencies\n // don't auto-install and we can skip them\n if (!isOptionalPeerDependencyMeta(peerDependenciesMeta, dependencyName)) {\n dependencies[dependencyName] = '';\n }\n }\n }\n\n const resolveDependency = async (\n dependencyName: string\n ): Promise => {\n for (let idx = 0; idx < nodeModulePaths.length; idx++) {\n const originPath = fastJoin(nodeModulePaths[idx], dependencyName);\n const nodeModulePath = await maybeRealpath(originPath);\n if (nodeModulePath != null) {\n return {\n source: DependencyResolutionSource.RECURSIVE_RESOLUTION,\n name: dependencyName,\n version: '',\n path: nodeModulePath,\n originPath,\n duplicates: null,\n depth,\n };\n }\n }\n return null;\n };\n\n const modules = await taskAll(\n Object.keys(dependencies).filter((dependencyName) => shouldIncludeDependency(dependencyName)),\n (dependencyName) => resolveDependency(dependencyName)\n );\n\n return modules.filter((resolution) => resolution != null);\n}\n\ninterface ResolutionOptions {\n shouldIncludeDependency?(name: string): boolean;\n limitDepth?: number;\n}\n\nexport async function scanDependenciesRecursively(\n rawPath: string,\n { shouldIncludeDependency = defaultShouldIncludeDependency, limitDepth }: ResolutionOptions = {}\n): Promise {\n const rootPath = await maybeRealpath(rawPath);\n if (!rootPath) {\n return {};\n }\n\n const _visitedPackagePaths = new Set();\n const getNodeModulePaths = createNodeModulePathsCreator();\n const maxDepth = limitDepth != null ? limitDepth : MAX_DEPTH;\n\n const recurse = async (\n resolution: DependencyResolution,\n depth = 0\n ): Promise => {\n const searchResults: ResolutionResult = Object.create(null);\n const hasVisitedPath = _visitedPackagePaths.has(resolution.path);\n _visitedPackagePaths.add(resolution.path);\n const [nodeModulePaths, packageJson] = await Promise.all([\n !hasVisitedPath ? getNodeModulePaths(resolution.path) : [],\n loadPackageJson(fastJoin(resolution.path, 'package.json')),\n ]);\n if (!packageJson) {\n return searchResults;\n } else {\n resolution.version = packageJson.version || '';\n }\n\n if (!hasVisitedPath && depth + 1 < maxDepth) {\n const modules = await resolveDependencies(\n packageJson,\n nodeModulePaths,\n depth,\n shouldIncludeDependency\n );\n for (let idx = 0; idx < modules.length; idx++) {\n searchResults[modules[idx].name] = modules[idx];\n }\n const childResults = await taskAll(modules, (resolution) => recurse(resolution, depth + 1));\n return mergeResolutionResults(childResults, searchResults);\n } else {\n return searchResults;\n }\n };\n\n const searchResults = await recurse({\n source: DependencyResolutionSource.RECURSIVE_RESOLUTION,\n name: '',\n version: '',\n path: rootPath,\n originPath: rawPath,\n duplicates: null,\n depth: -1,\n });\n\n return searchResults;\n}\n\nconst isOptionalPeerDependencyMeta = (\n peerDependenciesMeta: Record | undefined,\n packageName: string\n) => {\n return (\n peerDependenciesMeta &&\n peerDependenciesMeta[packageName] != null &&\n typeof peerDependenciesMeta[packageName] === 'object' &&\n 'optional' in peerDependenciesMeta[packageName] &&\n !!peerDependenciesMeta[packageName].optional\n );\n};\n"]} \ No newline at end of file diff --git a/packages/expo-modules-autolinking/build/dependencies/utils.js b/packages/expo-modules-autolinking/build/dependencies/utils.js index 74f31585afde14..2a4a4862969e21 100644 --- a/packages/expo-modules-autolinking/build/dependencies/utils.js +++ b/packages/expo-modules-autolinking/build/dependencies/utils.js @@ -70,6 +70,14 @@ function mergeWithDuplicate(a, b) { target = b; duplicate = a; } + else if (b.source < a.source) { + target = b; + duplicate = a; + } + else if (b.originPath < a.originPath) { + target = b; + duplicate = a; + } else { target = a; duplicate = b; diff --git a/packages/expo-modules-autolinking/build/dependencies/utils.js.map b/packages/expo-modules-autolinking/build/dependencies/utils.js.map index d574ea70bcb171..b31f5d9dd1e823 100644 --- a/packages/expo-modules-autolinking/build/dependencies/utils.js.map +++ b/packages/expo-modules-autolinking/build/dependencies/utils.js.map @@ -1 +1 @@ -{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/dependencies/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'path';\n\nimport { taskAll } from '../concurrency';\nimport {\n DependencyResolutionSource,\n type DependencyResolution,\n type ResolutionResult,\n} from './types';\n\nconst NODE_MODULES_PATTERN = `${path.sep}node_modules${path.sep}`;\n\n// The default dependencies we exclude don't contain dependency chains leading to autolinked modules\nexport function defaultShouldIncludeDependency(dependencyName: string): boolean {\n const scopeName =\n dependencyName[0] === '@' ? dependencyName.slice(1, dependencyName.indexOf('/')) : null;\n if (\n scopeName === 'babel' ||\n scopeName === 'types' ||\n scopeName === 'eslint' ||\n scopeName === 'typescript-eslint' ||\n scopeName === 'testing-library' ||\n scopeName === 'aws-crypto' ||\n scopeName === 'aws-sdk'\n ) {\n return false;\n }\n switch (dependencyName) {\n case '@expo/cli':\n case '@expo/config':\n case '@expo/metro-config':\n case '@expo/package-manager':\n case '@expo/prebuild-config':\n case '@expo/webpack-config':\n case '@expo/env':\n case '@react-native/codegen':\n case '@react-native/community-cli-plugin':\n case 'eslint':\n case 'eslint-config-expo':\n case 'eslint-plugin-expo':\n case 'eslint-plugin-import':\n case 'jest-expo':\n case 'jest':\n case 'metro':\n case 'ts-node':\n case 'typescript':\n case 'webpack':\n return false;\n default:\n return true;\n }\n}\n\nexport function mergeWithDuplicate(\n a: DependencyResolution,\n b: DependencyResolution\n): DependencyResolution {\n let target: DependencyResolution;\n let duplicate: DependencyResolution;\n if (a.depth < b.depth) {\n target = a;\n duplicate = b;\n } else if (b.depth < a.depth) {\n target = b;\n duplicate = a;\n } else {\n // If both are equal, then the shallowest path wins\n const pathDepthA = a.originPath.split(NODE_MODULES_PATTERN).length;\n const pathDepthB = b.originPath.split(NODE_MODULES_PATTERN).length;\n if (pathDepthA < pathDepthB) {\n target = a;\n duplicate = b;\n } else if (pathDepthB < pathDepthA) {\n target = b;\n duplicate = a;\n } else {\n target = a;\n duplicate = b;\n }\n }\n const duplicates = target.duplicates || (target.duplicates = []);\n if (target.path !== duplicate.path) {\n if (duplicates.every((parent) => parent.path !== duplicate.path)) {\n duplicates.push({\n name: duplicate.name,\n version: duplicate.version,\n path: duplicate.path,\n originPath: duplicate.originPath,\n });\n }\n } else if (!target.version && duplicate.version) {\n target.version = duplicate.version;\n }\n if (duplicate.duplicates?.length) {\n duplicates.push(\n ...duplicate.duplicates.filter((child) =>\n duplicates.every((parent) => parent.path !== child.path)\n )\n );\n }\n return target;\n}\n\nexport async function filterMapResolutionResult(\n results: ResolutionResult,\n filterMap: (resolution: DependencyResolution) => Promise | T | null\n): Promise> {\n const resolutions = await taskAll(Object.keys(results), async (key) => {\n const resolution = results[key];\n const result = resolution ? await filterMap(resolution) : null;\n // If we failed to find a matching resolution from `searchPaths`, also try the other duplicates\n // to see if the `searchPaths` result is not a module but another is\n if (resolution?.source === DependencyResolutionSource.SEARCH_PATH && !result) {\n for (let idx = 0; resolution.duplicates && idx < resolution.duplicates.length; idx++) {\n const duplicate = resolution.duplicates[idx];\n const duplicateResult = await filterMap({ ...resolution, ...duplicate });\n if (duplicateResult != null) {\n return duplicateResult;\n }\n }\n }\n return result;\n });\n const output: Record = Object.create(null);\n for (let idx = 0; idx < resolutions.length; idx++) {\n const resolution = resolutions[idx];\n if (resolution != null) {\n output[resolution.name] = resolution;\n }\n }\n return output;\n}\n\nexport function mergeResolutionResults(\n results: ResolutionResult[],\n base?: ResolutionResult\n): ResolutionResult {\n if (base == null && results.length === 1) {\n return results[0];\n }\n const output: ResolutionResult = base == null ? Object.create(null) : base;\n for (let idx = 0; idx < results.length; idx++) {\n for (const key in results[idx]) {\n const resolution = results[idx][key]!;\n const prevResolution = output[key];\n if (prevResolution != null) {\n output[key] = mergeWithDuplicate(prevResolution, resolution);\n } else {\n output[key] = resolution;\n }\n }\n }\n return output;\n}\n"]} \ No newline at end of file +{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/dependencies/utils.ts"],"names":[],"mappings":"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","sourcesContent":["import path from 'path';\n\nimport { taskAll } from '../concurrency';\nimport {\n DependencyResolutionSource,\n type DependencyResolution,\n type ResolutionResult,\n} from './types';\n\nconst NODE_MODULES_PATTERN = `${path.sep}node_modules${path.sep}`;\n\n// The default dependencies we exclude don't contain dependency chains leading to autolinked modules\nexport function defaultShouldIncludeDependency(dependencyName: string): boolean {\n const scopeName =\n dependencyName[0] === '@' ? dependencyName.slice(1, dependencyName.indexOf('/')) : null;\n if (\n scopeName === 'babel' ||\n scopeName === 'types' ||\n scopeName === 'eslint' ||\n scopeName === 'typescript-eslint' ||\n scopeName === 'testing-library' ||\n scopeName === 'aws-crypto' ||\n scopeName === 'aws-sdk'\n ) {\n return false;\n }\n switch (dependencyName) {\n case '@expo/cli':\n case '@expo/config':\n case '@expo/metro-config':\n case '@expo/package-manager':\n case '@expo/prebuild-config':\n case '@expo/webpack-config':\n case '@expo/env':\n case '@react-native/codegen':\n case '@react-native/community-cli-plugin':\n case 'eslint':\n case 'eslint-config-expo':\n case 'eslint-plugin-expo':\n case 'eslint-plugin-import':\n case 'jest-expo':\n case 'jest':\n case 'metro':\n case 'ts-node':\n case 'typescript':\n case 'webpack':\n return false;\n default:\n return true;\n }\n}\n\nexport function mergeWithDuplicate(\n a: DependencyResolution,\n b: DependencyResolution\n): DependencyResolution {\n let target: DependencyResolution;\n let duplicate: DependencyResolution;\n if (a.depth < b.depth) {\n target = a;\n duplicate = b;\n } else if (b.depth < a.depth) {\n target = b;\n duplicate = a;\n } else {\n // If both are equal, then the shallowest path wins\n const pathDepthA = a.originPath.split(NODE_MODULES_PATTERN).length;\n const pathDepthB = b.originPath.split(NODE_MODULES_PATTERN).length;\n if (pathDepthA < pathDepthB) {\n target = a;\n duplicate = b;\n } else if (pathDepthB < pathDepthA) {\n target = b;\n duplicate = a;\n } else if (b.source < a.source) {\n target = b;\n duplicate = a;\n } else if (b.originPath < a.originPath) {\n target = b;\n duplicate = a;\n } else {\n target = a;\n duplicate = b;\n }\n }\n const duplicates = target.duplicates || (target.duplicates = []);\n if (target.path !== duplicate.path) {\n if (duplicates.every((parent) => parent.path !== duplicate.path)) {\n duplicates.push({\n name: duplicate.name,\n version: duplicate.version,\n path: duplicate.path,\n originPath: duplicate.originPath,\n });\n }\n } else if (!target.version && duplicate.version) {\n target.version = duplicate.version;\n }\n if (duplicate.duplicates?.length) {\n duplicates.push(\n ...duplicate.duplicates.filter((child) =>\n duplicates.every((parent) => parent.path !== child.path)\n )\n );\n }\n return target;\n}\n\nexport async function filterMapResolutionResult(\n results: ResolutionResult,\n filterMap: (resolution: DependencyResolution) => Promise | T | null\n): Promise> {\n const resolutions = await taskAll(Object.keys(results), async (key) => {\n const resolution = results[key];\n const result = resolution ? await filterMap(resolution) : null;\n // If we failed to find a matching resolution from `searchPaths`, also try the other duplicates\n // to see if the `searchPaths` result is not a module but another is\n if (resolution?.source === DependencyResolutionSource.SEARCH_PATH && !result) {\n for (let idx = 0; resolution.duplicates && idx < resolution.duplicates.length; idx++) {\n const duplicate = resolution.duplicates[idx];\n const duplicateResult = await filterMap({ ...resolution, ...duplicate });\n if (duplicateResult != null) {\n return duplicateResult;\n }\n }\n }\n return result;\n });\n const output: Record = Object.create(null);\n for (let idx = 0; idx < resolutions.length; idx++) {\n const resolution = resolutions[idx];\n if (resolution != null) {\n output[resolution.name] = resolution;\n }\n }\n return output;\n}\n\nexport function mergeResolutionResults(\n results: ResolutionResult[],\n base?: ResolutionResult\n): ResolutionResult {\n if (base == null && results.length === 1) {\n return results[0];\n }\n const output: ResolutionResult = base == null ? Object.create(null) : base;\n for (let idx = 0; idx < results.length; idx++) {\n for (const key in results[idx]) {\n const resolution = results[idx][key]!;\n const prevResolution = output[key];\n if (prevResolution != null) {\n output[key] = mergeWithDuplicate(prevResolution, resolution);\n } else {\n output[key] = resolution;\n }\n }\n }\n return output;\n}\n"]} \ No newline at end of file diff --git a/packages/expo-modules-autolinking/src/dependencies/__tests__/resolution-test.ts b/packages/expo-modules-autolinking/src/dependencies/__tests__/resolution-test.ts index 020a13f2ca24cd..a1b3cd0c5181b9 100644 --- a/packages/expo-modules-autolinking/src/dependencies/__tests__/resolution-test.ts +++ b/packages/expo-modules-autolinking/src/dependencies/__tests__/resolution-test.ts @@ -412,19 +412,7 @@ describe(scanDependenciesRecursively, () => { const result = await scanDependenciesRecursively(projectRoot, { limitDepth: 1 }); - expect(result).toMatchInlineSnapshot(` - { - "react-native-third-party": { - "depth": 0, - "duplicates": null, - "name": "react-native-third-party", - "originPath": "/fake/project/node_modules/react-native-third-party", - "path": "/fake/project/node_modules/react-native-third-party", - "source": 0, - "version": "", - }, - } - `); + expect(result).toMatchInlineSnapshot(`{}`); }); itWithMemoize('discovers transitive peer dependencies', async () => { @@ -530,7 +518,7 @@ describe(scanDependenciesRecursively, () => { expect(resultAB['dep']?.version).toBe('2.0.0'); }); - it('resolves multi-level isolated duplicates with stable path and version, unstable originPath', async () => { + it('resolves multi-level isolated duplicates with stable path and version, stable originPath', async () => { const runWithOrder = async (first: string, second: string) => { const result = await createMemoizer().withMemoizer(async () => { vol.fromNestedJSON( @@ -595,7 +583,7 @@ describe(scanDependenciesRecursively, () => { } expect(resultAB['shared-dep']?.version).toBe('2.0.0'); expect(resultAB['grandchild']?.version).toBe('3.0.0'); - expect(resultAB['shared-dep']?.originPath).not.toBe(resultBA['shared-dep']?.originPath); + expect(resultAB['shared-dep']?.originPath).toBe(resultBA['shared-dep']?.originPath); }); it('resolves mixed-depth diamond with stable path and version', async () => { @@ -774,4 +762,32 @@ describe(scanDependenciesRecursively, () => { } `); }); + + itWithMemoize('populates versions while stopping recursion at deepest dependency', async () => { + vol.fromNestedJSON( + { + ...mockedNodeModule('root', { + pkgDependencies: { 'parent-a': '*', 'parent-b': '*' }, + }), + node_modules: { + 'parent-a': mockedNodeModule('parent-a', { + pkgDependencies: { 'shared-dep': '*' }, + }), + 'parent-b': mockedNodeModule('parent-b', { + pkgDependencies: { 'shared-dep': '*' }, + }), + 'shared-dep': mockedNodeModule('shared-dep', { + pkgVersion: '6.0.0', + }), + }, + }, + projectRoot + ); + + // limitDepth: 2 prevents recurse from visiting shared-dep (discovered at depth 1). + // Without the eager-version fix in resolveDependency, version would be ''. + const result = await scanDependenciesRecursively(projectRoot, { limitDepth: 2 }); + + expect(result['shared-dep']).toBe(undefined); + }); }); diff --git a/packages/expo-modules-autolinking/src/dependencies/resolution.ts b/packages/expo-modules-autolinking/src/dependencies/resolution.ts index 451e92408498f7..ba17e0a7325a5f 100644 --- a/packages/expo-modules-autolinking/src/dependencies/resolution.ts +++ b/packages/expo-modules-autolinking/src/dependencies/resolution.ts @@ -15,7 +15,7 @@ declare module 'node:module' { // NOTE(@kitten): There's no need to search very deep for modules // We don't expect native modules to be excessively nested in the dependency tree -const MAX_DEPTH = 8; +const MAX_DEPTH = 9; const createNodeModulePathsCreator = () => { const _nodeModulePathCache = new Map(); @@ -125,13 +125,10 @@ export async function scanDependenciesRecursively( depth = 0 ): Promise => { const searchResults: ResolutionResult = Object.create(null); - if (_visitedPackagePaths.has(resolution.path)) { - return searchResults; - } else { - _visitedPackagePaths.add(resolution.path); - } + const hasVisitedPath = _visitedPackagePaths.has(resolution.path); + _visitedPackagePaths.add(resolution.path); const [nodeModulePaths, packageJson] = await Promise.all([ - getNodeModulePaths(resolution.path), + !hasVisitedPath ? getNodeModulePaths(resolution.path) : [], loadPackageJson(fastJoin(resolution.path, 'package.json')), ]); if (!packageJson) { @@ -140,17 +137,16 @@ export async function scanDependenciesRecursively( resolution.version = packageJson.version || ''; } - const modules = await resolveDependencies( - packageJson, - nodeModulePaths, - depth, - shouldIncludeDependency - ); - for (let idx = 0; idx < modules.length; idx++) { - searchResults[modules[idx].name] = modules[idx]; - } - - if (depth + 1 < maxDepth) { + if (!hasVisitedPath && depth + 1 < maxDepth) { + const modules = await resolveDependencies( + packageJson, + nodeModulePaths, + depth, + shouldIncludeDependency + ); + for (let idx = 0; idx < modules.length; idx++) { + searchResults[modules[idx].name] = modules[idx]; + } const childResults = await taskAll(modules, (resolution) => recurse(resolution, depth + 1)); return mergeResolutionResults(childResults, searchResults); } else { diff --git a/packages/expo-modules-autolinking/src/dependencies/utils.ts b/packages/expo-modules-autolinking/src/dependencies/utils.ts index 0f42f989dac742..0dd2fe8dfc90de 100644 --- a/packages/expo-modules-autolinking/src/dependencies/utils.ts +++ b/packages/expo-modules-autolinking/src/dependencies/utils.ts @@ -72,6 +72,12 @@ export function mergeWithDuplicate( } else if (pathDepthB < pathDepthA) { target = b; duplicate = a; + } else if (b.source < a.source) { + target = b; + duplicate = a; + } else if (b.originPath < a.originPath) { + target = b; + duplicate = a; } else { target = a; duplicate = b; From 63c0f2be182a78f90303475ff33f53a73177b526 Mon Sep 17 00:00:00 2001 From: Aman Mittal Date: Fri, 6 Mar 2026 19:07:46 +0530 Subject: [PATCH 4/7] [docs] Migrate to TailwindCSS v4 and upgrade styleguide libraries (#43623) # Why Fix ENG-19921 This migration updates the docs from Tailwind CSS v3 to v4, along with the companion `@expo/styleguide` packages that were updated for TW4 compatibility. # How **Core migration:** - Upgrade `tailwindcss` from v3 to v4, replace `postcss-import` and `autoprefixer` with `@tailwindcss/postcss` - Upgrade `@expo/styleguide` v9 to v10, `@expo/styleguide-icons` v2 to v3, `@expo/styleguide-search-ui` v3 to v4 - Convert `global.css` from `@tailwind` directives to `@import "tailwindcss"` with `@config` reference - Replace `eslint-plugin-tailwindcss` (TW3 only) with `eslint-plugin-better-tailwindcss` (TW4 compatible), following `@expo/styleguide`. **TW4 syntax conversions** - Convert TW3 important prefix syntax (`!p-0`, `!mb-0`) to TW4 suffix syntax (`p-0!`, `mb-0!`) - Convert TW3 gradient classes (`bg-gradient-to-b`) to TW4 syntax (`bg-linear-to-b`) - Reorder classes to match TW4 canonical ordering (handled by linter) **TW4 variant composition fixes:** - Fix `last:[&>*]:mb-0!` pattern in 4 files (`APICommentTextBlock`, `APIBox`, `Collapsible`, `Requirement`). TW4 generates two selectors for this pattern, one of which targets ALL children when the parent is `:last-child`, instead of only the last child. Changed to `[&>*:last-child]:mb-0!` which generates a single correct selector. - Fix `last:[&>div>*]:mb-0!` and `first:[&_figure]:mt-1`/`first:[&_pre]:mt-1` in `Tabs.tsx` with the same approach. - Remove `even:[&_blockquote]:bg-default` and `even:[&_summary]:bg-element` from `Table/Row.tsx`. In TW3, these generated selectors that never matched (e.g., `blockquote:nth-child(2n)` instead of the intended parent-scoped selector). In TW4, they became active and incorrectly overrode callout background colors (like deprecated notice yellow backgrounds) on even table rows. **Base layer fix:** - Wrap bare HTML styles (`html`, `body`, `img`, `input`) in `@layer base` so TW4 utility classes can properly override them. # Test Plan Run docs locally using `yarn run dev` and: - Check the homepage, sidebar navigation, and Ask AI overlay panel for visual correctness. - Check large pages such as Expo Router and Expo Camera reference, Set up your environment guide, app config reference, etc. where multiple components are used. - Compare the local dev site against `docs.expo.dev` for any visual regressions. # Checklist - [ ] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --------- Co-authored-by: Aman Mittal <10234615+amandeepmittal@users.noreply.github.com> --- .../DocumentationNestedScrollLayout.tsx | 12 +- docs/components/DocumentationPage.tsx | 4 +- docs/components/ScrollContainer.tsx | 2 +- docs/components/base/code.tsx | 12 +- docs/components/plugins/APIBox.tsx | 2 +- docs/components/plugins/CodeBlocksTable.tsx | 6 +- docs/components/plugins/EasMetadataTable.tsx | 2 +- .../__snapshots__/APISection.test.tsx.snap | 2258 ++++++++--------- .../plugins/api/APISectionClasses.tsx | 6 +- .../plugins/api/APISectionComponents.tsx | 2 +- .../plugins/api/APISectionDeprecationNote.tsx | 2 +- .../plugins/api/APISectionEnums.tsx | 10 +- .../plugins/api/APISectionMethods.tsx | 10 +- .../plugins/api/APISectionProps.tsx | 8 +- .../plugins/api/APISectionTypes.tsx | 12 +- .../plugins/api/APISectionUtils.tsx | 4 +- .../APISectionUtils.test.tsx.snap | 48 +- .../plugins/api/components/APIBoxHeader.tsx | 6 +- .../api/components/APIBoxSectionHeader.tsx | 4 +- .../api/components/APICommentTextBlock.tsx | 12 +- .../plugins/api/components/APIDataType.tsx | 2 +- .../api/components/APIParamDetailsBlock.tsx | 2 +- .../plugins/api/components/APIParamRow.tsx | 2 +- .../api/components/APISectionPlatformTags.tsx | 2 +- .../APICommentTextBlock.test.tsx.snap | 28 +- docs/components/plugins/api/styles.tsx | 10 +- .../permissions/AndroidPermissions.tsx | 6 +- docs/eslint.config.mjs | 36 +- docs/package.json | 17 +- docs/pages/_error.tsx | 6 +- .../unversioned/sdk/safe-area-context.mdx | 2 +- .../v53.0.0/sdk/safe-area-context.mdx | 2 +- .../v54.0.0/sdk/safe-area-context.mdx | 2 +- .../v55.0.0/sdk/safe-area-context.mdx | 2 +- .../BuildEnvironmentSwitch.tsx | 2 +- .../set-up-your-environment/SelectCard.tsx | 10 +- .../instructions/androidPhysicalExpoGo.mdx | 2 +- .../start-developing/ProjectStructure/Tab.tsx | 4 +- .../ProjectStructure/index.tsx | 8 +- .../TemplateFeatures/Content.tsx | 4 +- .../start-developing/TemplateFeatures/Tab.tsx | 4 +- .../TemplateFeatures/index.tsx | 8 +- docs/styles/global.css | 45 +- docs/tailwind.config.cjs | 4 +- .../AppConfigSchemaTable.test.tsx.snap | 148 +- .../components/AppConfigSchemaTable/index.tsx | 12 +- docs/ui/components/AppJSBanner/index.tsx | 8 +- .../ui/components/AskPageAI/AskPageAIChat.tsx | 2 +- .../AskPageAI/AskPageAIChatHeader.tsx | 16 +- .../AskPageAI/AskPageAIChatInput.tsx | 8 +- .../AskPageAI/AskPageAIChatMessages.tsx | 32 +- .../components/AskPageAI/AskPageAIOverlay.tsx | 7 +- .../AskPageAI/useChatMarkdownComponents.tsx | 22 +- docs/ui/components/Authentication/Box.tsx | 2 +- .../ui/components/Authentication/GridItem.tsx | 4 +- docs/ui/components/Authentication/Icon.tsx | 2 +- docs/ui/components/BoxLink/index.tsx | 8 +- docs/ui/components/Collapsible/index.tsx | 15 +- .../ConfigPluginHierarchy/index.tsx | 6 +- .../ConfigSection/ConfigPluginProperties.tsx | 4 +- docs/ui/components/ContentSpotlight/index.tsx | 8 +- docs/ui/components/CopyableText/index.tsx | 4 +- docs/ui/components/Diagram/Diagram.tsx | 4 +- docs/ui/components/DownloadSlide/index.tsx | 14 +- docs/ui/components/Dropdown/Item.tsx | 8 +- docs/ui/components/Dropdown/index.tsx | 6 +- docs/ui/components/EASCLIReference/index.tsx | 14 +- .../components/EASHostingShoutoutBanner.tsx | 14 +- docs/ui/components/FileTree/TextWithNote.tsx | 2 +- docs/ui/components/FileTree/index.tsx | 10 +- docs/ui/components/Footer/FeedbackDialog.tsx | 14 +- docs/ui/components/Footer/Footer.tsx | 14 +- .../ui/components/Footer/NewsletterSignUp.tsx | 8 +- docs/ui/components/Footer/PageVote.tsx | 6 +- docs/ui/components/Form/Checkbox.tsx | 6 +- docs/ui/components/Form/Input.tsx | 2 +- docs/ui/components/Form/Textarea.tsx | 4 +- docs/ui/components/Header/Header.tsx | 14 +- docs/ui/components/Header/Logo.tsx | 16 +- .../ui/components/Home/components/GridBox.tsx | 6 +- .../components/Home/components/GridCell.tsx | 4 +- docs/ui/components/Home/components/Header.tsx | 4 +- .../Home/resources/CodecademyImage.tsx | 2 +- .../Home/resources/DevicesImage.tsx | 2 +- .../Home/resources/QuickStartIcon.tsx | 2 +- .../components/Home/resources/SnackImage.tsx | 2 +- .../Home/sections/CommandLineTools.tsx | 12 +- .../components/Home/sections/DiscoverMore.tsx | 36 +- .../components/Home/sections/ExploreAPIs.tsx | 14 +- .../Home/sections/ExploreExamples.tsx | 8 +- .../Home/sections/JoinTheCommunity.tsx | 8 +- .../components/Home/sections/QuickStart.tsx | 42 +- docs/ui/components/Home/sections/Talks.tsx | 16 +- docs/ui/components/InlineHelp/index.tsx | 6 +- .../LaunchPartyBanner/ConfettiPopper.tsx | 2 +- .../ui/components/LaunchPartyBanner/index.tsx | 16 +- docs/ui/components/Layout/Layout.tsx | 4 +- docs/ui/components/Layout/LayoutScroll.tsx | 2 +- .../MarkdownActionsDropdown.tsx | 2 +- docs/ui/components/Navigation/GroupList.tsx | 2 +- docs/ui/components/Navigation/Navigation.tsx | 2 +- docs/ui/components/Navigation/PageLink.tsx | 6 +- docs/ui/components/Navigation/SectionList.tsx | 4 +- .../components/PageHeader/PageCliVersion.tsx | 2 +- docs/ui/components/PageHeader/PageHeader.tsx | 17 +- .../PageHeader/PagePackageVersion.tsx | 2 +- .../PageHeader/PagePlatformTags.tsx | 2 +- .../PageHeader/PageTitleButtons.tsx | 8 +- docs/ui/components/Permalink/Permalink.tsx | 2 +- .../PossibleRedirectNotification.tsx | 2 +- .../components/Prerequisites/Requirement.tsx | 4 +- docs/ui/components/Prerequisites/index.tsx | 8 +- .../ProgressTracker/SuccessCheckmark.tsx | 2 +- docs/ui/components/ProgressTracker/index.tsx | 8 +- .../ReactNavigationOptions/index.tsx | 2 +- docs/ui/components/RouteUrl/RuntimePopup.tsx | 6 +- .../ReactNativeCompatibilityTable.tsx | 2 +- docs/ui/components/Search/Search.tsx | 2 +- docs/ui/components/Select.tsx | 18 +- docs/ui/components/Separator.tsx | 4 +- .../components/Sidebar/ApiVersionSelect.tsx | 2 +- docs/ui/components/Sidebar/Sidebar.tsx | 4 +- .../components/Sidebar/SidebarCollapsible.tsx | 4 +- docs/ui/components/Sidebar/SidebarFooter.tsx | 2 +- docs/ui/components/Sidebar/SidebarGroup.tsx | 12 +- docs/ui/components/Sidebar/SidebarHead.tsx | 6 +- docs/ui/components/Sidebar/SidebarLink.tsx | 28 +- .../components/Sidebar/SidebarSingleEntry.tsx | 6 +- docs/ui/components/Snippet/FileStatus.tsx | 2 +- docs/ui/components/Snippet/SnippetAction.tsx | 6 +- docs/ui/components/Snippet/SnippetContent.tsx | 10 +- .../Snippet/SnippetExpandOverlay.tsx | 2 +- docs/ui/components/Snippet/SnippetHeader.tsx | 8 +- .../Snippet/actions/SettingsAction.tsx | 2 +- .../components/Snippet/blocks/DiffBlock.tsx | 2 +- .../components/Snippet/blocks/SnackInline.tsx | 2 +- .../ui/components/Snippet/blocks/Terminal.tsx | 12 +- docs/ui/components/StateOfRNBanner/index.tsx | 6 +- docs/ui/components/Step/Step.tsx | 8 +- docs/ui/components/Switch/index.tsx | 6 +- docs/ui/components/Table/Cell.tsx | 2 +- docs/ui/components/Table/HeaderCell.tsx | 4 +- docs/ui/components/Table/Row.tsx | 10 +- docs/ui/components/Table/Table.tsx | 4 +- docs/ui/components/Table/TableHead.tsx | 2 +- .../TableOfContents/TableOfContents.test.tsx | 6 +- .../TableOfContents/TableOfContents.tsx | 10 +- .../TableOfContents/TableOfContentsLink.tsx | 10 +- .../TableOfContents.test.tsx.snap | 18 +- docs/ui/components/Tabs/TabButton.tsx | 4 +- docs/ui/components/Tabs/Tabs.tsx | 8 +- docs/ui/components/Tag/PlatformIcon.tsx | 6 +- docs/ui/components/Tag/PlatformTag.tsx | 4 +- docs/ui/components/Tag/PlatformTags.tsx | 2 +- docs/ui/components/Tag/SDKRangeTag.tsx | 4 +- docs/ui/components/Tag/StatusTag.tsx | 4 +- .../TemplateBareMinimumDiffViewer/index.tsx | 4 +- docs/ui/components/Text/index.tsx | 32 +- docs/ui/components/Tooltip/Content.tsx | 2 +- docs/ui/components/VideoBoxLink/index.tsx | 12 +- docs/yarn.lock | 826 +++--- 161 files changed, 2330 insertions(+), 2165 deletions(-) diff --git a/docs/components/DocumentationNestedScrollLayout.tsx b/docs/components/DocumentationNestedScrollLayout.tsx index ec30cb790bfaf3..74e28064c35166 100644 --- a/docs/components/DocumentationNestedScrollLayout.tsx +++ b/docs/components/DocumentationNestedScrollLayout.tsx @@ -84,7 +84,7 @@ export default function DocumentationNestedScrollLayout({
{header}
-
+
{onSidebarToggle ? (
{children}
@@ -167,7 +167,7 @@ export default function DocumentationNestedScrollLayout({ {!hideTOC && (
diff --git a/docs/components/DocumentationPage.tsx b/docs/components/DocumentationPage.tsx index 721e3b9b19bbb9..95bea32b7672fd 100644 --- a/docs/components/DocumentationPage.tsx +++ b/docs/components/DocumentationPage.tsx @@ -341,7 +341,7 @@ export default function DocumentationPage({
{version && version === 'unversioned' && ( - + This is documentation for the next SDK version. For up-to-date documentation, see the{' '} latest version ( {versionToText(LATEST_VERSION)}). diff --git a/docs/components/ScrollContainer.tsx b/docs/components/ScrollContainer.tsx index 42cf62d92ee6cb..b967e2643a8ccc 100644 --- a/docs/components/ScrollContainer.tsx +++ b/docs/components/ScrollContainer.tsx @@ -41,7 +41,7 @@ export const ScrollContainer = forwardRef diff --git a/docs/components/plugins/APIBox.tsx b/docs/components/plugins/APIBox.tsx index 9b8fa43ca28847..866f958e73ab72 100644 --- a/docs/components/plugins/APIBox.tsx +++ b/docs/components/plugins/APIBox.tsx @@ -33,7 +33,7 @@ export const APIBox = ({ STYLES_APIBOX_WRAPPER, headerNestingLevel > 3 && STYLES_APIBOX_NESTED, className, - '!pb-4 last:[&>*]:!mb-1' + 'pb-4! [&>*:last-child]:mb-1!' )}> {header && ( diff --git a/docs/components/plugins/CodeBlocksTable.tsx b/docs/components/plugins/CodeBlocksTable.tsx index 87640592f0ce7b..d594acdf88eb65 100644 --- a/docs/components/plugins/CodeBlocksTable.tsx +++ b/docs/components/plugins/CodeBlocksTable.tsx @@ -41,14 +41,14 @@ export function CodeBlocksTable({ children, tabs, connected = true, ...rest }: P 'grid grid-cols-2 gap-4', connected && 'lg-gutters:mb-4 lg-gutters:gap-0', connected && - '[&>div:nth-child(odd)>div]:lg-gutters:!rounded-r-none [&>div:nth-child(odd)>div]:lg-gutters:border-r-0', - connected && '[&>div:nth-child(even)>div]:lg-gutters:!rounded-l-none', + '[&>div:nth-child(odd)>div]:lg-gutters:rounded-r-none! [&>div:nth-child(odd)>div]:lg-gutters:border-r-0', + connected && '[&>div:nth-child(even)>div]:lg-gutters:rounded-l-none!', '[&_pre]:m-0 [&_pre]:border-0', 'max-lg-gutters:grid-cols-1' )} {...rest}> {codeBlocks.map((codeBlock, index) => ( - + diff --git a/docs/components/plugins/EasMetadataTable.tsx b/docs/components/plugins/EasMetadataTable.tsx index 7644caad443ba0..fba63049f6a5aa 100644 --- a/docs/components/plugins/EasMetadataTable.tsx +++ b/docs/components/plugins/EasMetadataTable.tsx @@ -31,7 +31,7 @@ export function MetadataTable({ const id = useId(); return ( -
+
{children.map(property => ( diff --git a/docs/components/plugins/__snapshots__/APISection.test.tsx.snap b/docs/components/plugins/__snapshots__/APISection.test.tsx.snap index 889f442f962db5..ae68a04c147c85 100644 --- a/docs/components/plugins/__snapshots__/APISection.test.tsx.snap +++ b/docs/components/plugins/__snapshots__/APISection.test.tsx.snap @@ -3,13 +3,13 @@ exports[`APISection expo-apple-authentication 1`] = `

Component

AppleAuthenticationButtonProps @@ -129,10 +129,10 @@ exports[`APISection expo-apple-authentication 1`] = `

This component displays the proprietary "Sign In with Apple" / "Continue with Apple" button on @@ -143,16 +143,16 @@ available via the provided properties.

You should only attempt to render this if AppleAuthentication.isAvailableAsync() @@ -161,7 +161,7 @@ available via the provided properties. resolves to true @@ -169,7 +169,7 @@ resolves to . This component will render nothing if it is not available, and you will get a warning in development mode ( __DEV__ === true @@ -179,12 +179,12 @@ a warning in development mode (

The properties of this component extend from View @@ -192,21 +192,21 @@ a warning in development mode ( ; however, you should not attempt to set backgroundColor or borderRadius with the style @@ -214,7 +214,7 @@ a warning in development mode ( property. This will not work and is against the App Store Guidelines. Instead, you should use the buttonStyle @@ -222,7 +222,7 @@ the App Store Guidelines. Instead, you should use the property to choose one of the predefined color styles and the cornerRadius @@ -233,14 +233,14 @@ button.

Make sure to attach height and width via the style props as without these styles, the button will not appear on the screen.

@@ -270,21 +270,21 @@ not appear on the screen.
Parameter Type Description
@@ -1050,14 +1050,14 @@ properties. AppleAuthenticationFullName @@ -1065,13 +1065,13 @@ properties.

The full name object with the tokenized portions @@ -1080,33 +1080,33 @@ properties.

formatStyle (optional) AppleAuthenticationFullNameFormatStyle @@ -1114,13 +1114,13 @@ properties.

The style in which the name should be formatted @@ -1133,10 +1133,10 @@ properties.


Creates a locale-aware string representation of a person's name from an object representing the tokenized portions of a user's full name @@ -1149,7 +1149,7 @@ properties. class="flex flex-row items-center gap-2" > Returns:

string

A locale-aware string representation of a person's name @@ -1197,24 +1197,24 @@ properties.

getCredentialStateAsync(user)