diff --git a/CHANGELOG.md b/CHANGELOG.md
index a0fc0df..4944969 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -33,14 +33,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [`Instance:GetTags`](https://create.roblox.com/docs/reference/engine/classes/Instance#GetTags)
- [`Instance:HasTag`](https://create.roblox.com/docs/reference/engine/classes/Instance#HasTag)
- [`Instance:RemoveTag`](https://create.roblox.com/docs/reference/engine/classes/Instance#RemoveTag)
+- Implemented the second argument of the `FindFirstChild` / `FindFirstChildOfClass` / `FindFirstChildWhichIsA` instance methods.
### Removed
-- Removed `CollectionService` specific methods from the `roblox` builtin library:
- - [`CollectionService:AddTag`](https://create.roblox.com/docs/reference/engine/classes/CollectionService#AddTag)
- - [`CollectionService:GetTags`](https://create.roblox.com/docs/reference/engine/classes/CollectionService#GetTags)
- - [`CollectionService:HasTag`](https://create.roblox.com/docs/reference/engine/classes/CollectionService#HasTag)
- - [`CollectionService:RemoveTag`](https://create.roblox.com/docs/reference/engine/classes/CollectionService#RemoveTag)
+- Removed `CollectionService` and its methods from the `roblox` builtin library.
+- Removed [`Instance:FindFirstDescendant`](https://create.roblox.com/docs/reference/engine/classes/Instance#FindFirstDescendant) which was a method that was never enabled in the official Roblox API and will soon be removed.
+ Use the second argument of the already existing find methods instead to find descendants.
### Changed
diff --git a/docs/pages/roblox/Api-Status.md b/docs/pages/roblox/Api-Status.md
index 2ef8068..cc3aaa4 100644
--- a/docs/pages/roblox/Api-Status.md
+++ b/docs/pages/roblox/Api-Status.md
@@ -25,7 +25,6 @@ Currently implemented APIs:
- [`FindFirstChild`](https://create.roblox.com/docs/reference/engine/classes/Instance#FindFirstChild)
- [`FindFirstChildOfClass`](https://create.roblox.com/docs/reference/engine/classes/Instance#FindFirstChildOfClass)
- [`FindFirstChildWhichIsA`](https://create.roblox.com/docs/reference/engine/classes/Instance#FindFirstChildWhichIsA)
-- [`FindFirstDescendant`](https://create.roblox.com/docs/reference/engine/classes/Instance#FindFirstDescendant)
- [`GetAttribute`](https://create.roblox.com/docs/reference/engine/classes/Instance#GetAttribute)
- [`GetAttributes`](https://create.roblox.com/docs/reference/engine/classes/Instance#GetAttributes)
- [`GetChildren`](https://create.roblox.com/docs/reference/engine/classes/Instance#GetChildren)
diff --git a/packages/lib-roblox/src/instance/mod.rs b/packages/lib-roblox/src/instance/mod.rs
index aeea352..0eb302f 100644
--- a/packages/lib-roblox/src/instance/mod.rs
+++ b/packages/lib-roblox/src/instance/mod.rs
@@ -1064,24 +1064,43 @@ impl LuaUserData for Instance {
.to_lua(lua)
},
);
- methods.add_method("FindFirstChild", |lua, this, name: String| {
- this.ensure_not_destroyed()?;
- this.find_child(|child| child.name == name).to_lua(lua)
- });
- methods.add_method("FindFirstChildOfClass", |lua, this, class_name: String| {
- this.ensure_not_destroyed()?;
- this.find_child(|child| child.class == class_name)
- .to_lua(lua)
- });
- methods.add_method("FindFirstChildWhichIsA", |lua, this, class_name: String| {
- this.ensure_not_destroyed()?;
- this.find_child(|child| class_is_a(&child.class, &class_name).unwrap_or(false))
- .to_lua(lua)
- });
- methods.add_method("FindFirstDescendant", |lua, this, name: String| {
- this.ensure_not_destroyed()?;
- this.find_descendant(|child| child.name == name).to_lua(lua)
- });
+ methods.add_method(
+ "FindFirstChild",
+ |lua, this, (name, recursive): (String, Option)| {
+ this.ensure_not_destroyed()?;
+ let predicate = |child: &DomInstance| child.name == name;
+ if matches!(recursive, Some(true)) {
+ this.find_descendant(predicate).to_lua(lua)
+ } else {
+ this.find_child(predicate).to_lua(lua)
+ }
+ },
+ );
+ methods.add_method(
+ "FindFirstChildOfClass",
+ |lua, this, (class_name, recursive): (String, Option)| {
+ this.ensure_not_destroyed()?;
+ let predicate = |child: &DomInstance| child.class == class_name;
+ if matches!(recursive, Some(true)) {
+ this.find_descendant(predicate).to_lua(lua)
+ } else {
+ this.find_child(predicate).to_lua(lua)
+ }
+ },
+ );
+ methods.add_method(
+ "FindFirstChildWhichIsA",
+ |lua, this, (class_name, recursive): (String, Option)| {
+ this.ensure_not_destroyed()?;
+ let predicate =
+ |child: &DomInstance| class_is_a(&child.class, &class_name).unwrap_or(false);
+ if matches!(recursive, Some(true)) {
+ this.find_descendant(predicate).to_lua(lua)
+ } else {
+ this.find_child(predicate).to_lua(lua)
+ }
+ },
+ );
methods.add_method("IsA", |_, this, class_name: String| {
this.ensure_not_destroyed()?;
Ok(class_is_a(&this.class_name, class_name).unwrap_or(false))
diff --git a/packages/lib/src/tests.rs b/packages/lib/src/tests.rs
index f66232f..406a540 100644
--- a/packages/lib/src/tests.rs
+++ b/packages/lib/src/tests.rs
@@ -141,7 +141,6 @@ create_tests! {
roblox_instance_methods_find_first_child: "roblox/instance/methods/FindFirstChild",
roblox_instance_methods_find_first_child_of_class: "roblox/instance/methods/FindFirstChildOfClass",
roblox_instance_methods_find_first_child_which_is_a: "roblox/instance/methods/FindFirstChildWhichIsA",
- roblox_instance_methods_find_first_descendant: "roblox/instance/methods/FindFirstDescendant",
roblox_instance_methods_get_children: "roblox/instance/methods/GetChildren",
roblox_instance_methods_get_descendants: "roblox/instance/methods/GetDescendants",
roblox_instance_methods_get_full_name: "roblox/instance/methods/GetFullName",
diff --git a/tests/roblox/instance/methods/FindFirstDescendant.luau b/tests/roblox/instance/methods/FindFirstDescendant.luau
deleted file mode 100644
index 1b8e35b..0000000
--- a/tests/roblox/instance/methods/FindFirstDescendant.luau
+++ /dev/null
@@ -1,31 +0,0 @@
-local roblox = require("@lune/roblox") :: any
-local Instance = roblox.Instance
-
-local root = Instance.new("Folder")
-local inner1 = Instance.new("Model")
-local inner1_1 = Instance.new("Part")
-local inner1_2 = Instance.new("Part")
-local inner2 = Instance.new("Model")
-local inner2_1 = Instance.new("Part")
-local inner2_2 = Instance.new("Part")
-
-inner1.Parent = root
-inner1_1.Parent = inner1
-inner1_2.Parent = inner1
-inner2.Parent = root
-inner2_1.Parent = inner2
-inner2_2.Parent = inner2
-
-assert(root:FindFirstDescendant("Folder") == nil)
-assert(root:FindFirstDescendant("Model") == inner1)
-assert(root:FindFirstDescendant("Part") == inner1_1)
-
-inner1:Destroy()
-
-assert(root:FindFirstDescendant("Folder") == nil)
-assert(root:FindFirstDescendant("Model") == inner2)
-assert(root:FindFirstDescendant("Part") == inner2_1)
-
-inner2_1:Destroy()
-
-assert(root:FindFirstDescendant("Part") == inner2_2)