Skip to content

Conversation

@cnaples79
Copy link
Contributor

@cnaples79 cnaples79 commented Sep 13, 2025

Description

  • Current behavior: $findMatchingParent and $getAncestor logic lived in multiple places (@lexical/utils, @lexical/selection), leading to duplicated implementations and layering violations. This duplication also surfaced as duplicate symbol/implementation errors in CI when both versions were in play.

  • Changes in this PR:

    • Move a single, typed implementation of $findMatchingParent into core (packages/lexical/src/LexicalUtils.ts) with overloads (type‑predicate support).
    • Export $findMatchingParent from core (packages/lexical/src/index.ts).
    • @lexical/utils: re‑export $findMatchingParent from lexical and remove the duplicate implementation.
    • @lexical/selection: provide a local exported $getAncestor wrapper that delegates to core $findMatchingParent (import from lexical), avoiding a utils dependency and removing local duplication.
    • Remove stray PR‑only comment in @lexical/link (no functional change).

No runtime behavior changes are intended; this is a consolidation/refactor to respect package layering and eliminate duplicate code.

Closes #5311

Test plan

Before

  • Multiple implementations of ancestor lookup existed across packages.
  • CI surfaced TypeScript/esbuild errors like duplicate symbol/implementation for $findMatchingParent when both versions compiled.

After

  • A single source of truth for ancestor lookup in core, with utils re‑export and selection delegating wrapper.
  • Packages compile against the unified helper; behavior remains the same and is covered by existing unit/e2e tests (e.g., selection, links, lists, tables).

No visual changes.

…ils in place of duplicate implementations.\n- Replace local helper in lexical-link and wrap in lexical-selection to delegate to shared utility.\n- Reduces duplicate logic and aligns with guidance in facebook#5311.\n\nPartial fix for facebook#5311
@vercel
Copy link

vercel bot commented Sep 13, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
lexical Ready Ready Preview Comment Sep 14, 2025 4:49pm
lexical-playground Ready Ready Preview Comment Sep 14, 2025 4:49pm

@meta-cla
Copy link

meta-cla bot commented Sep 13, 2025

Hi @cnaples79!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at [email protected]. Thanks!

@meta-cla meta-cla bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 13, 2025
@meta-cla
Copy link

meta-cla bot commented Sep 13, 2025

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

INTERNAL_$isBlock,
} from 'lexical';
import invariant from 'shared/invariant';
import { $findMatchingParent } from '@lexical/utils';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lexical/selection can't depend on @lexical/utils

@etrepum
Copy link
Collaborator

etrepum commented Sep 13, 2025

The follow-up work, or equivalent, is necessary for this to work at all because @lexical/utils depends on @lexical/selection so you can't have @lexical/selection depend on @lexical/utils.

@cnaples79
Copy link
Contributor Author

@etrepum I see! Thanks for the feedback, I'll update the fix and PR.

@cnaples79
Copy link
Contributor Author

Thanks for the pointer on package layering, @etrepum. I’ve updated the PR to avoid the circular dependency by moving into core (), exporting it via , and re-exporting from .\n\nChanges: \n- Add to with the same typed overloads and docs.\n- Export from .\n- Update to import from (not ).\n- Re-export from and remove the duplicate local implementation.\n\nThis keeps free of a dependency on , removes the duplication, and aligns with the guidance from #5311. Happy to iterate further if you'd prefer a different placement or naming.

@cnaples79
Copy link
Contributor Author

Pushed a follow-up to fix the Vercel failure: removed the duplicate const and added proper overloads to the existing function in (single implementation). This should unblock the playground build. If there’s a preferred location or naming for the overloads/docs, happy to tweak.

@cnaples79
Copy link
Contributor Author

Found and fixed the Vercel build failure! 🎉

Issue: There were duplicate function declarations causing TypeScript compilation errors:

  • Overload declarations on lines 981-988 ✅
  • Duplicate export function declaration on line 1789 ❌

Fix: Removed the export keyword from line 1789. The function is already properly exported via the overload declarations above.

The TypeScript pattern should be:

  1. Export overload declarations
  2. Single implementation function (without export keyword)

This should resolve the build failure. The playground build was already working, so this was specifically affecting the main lexical package build.

}
return predicate(parent) ? parent : null;
}
// Removed duplicate $getAncestor in favor of shared $findMatchingParent from @lexical/utils
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this comment, only really makes sense in the context of the PR, not the remaining code

Suggested change
// Removed duplicate $getAncestor in favor of shared $findMatchingParent from @lexical/utils

}
return predicate(parent) ? parent : null;
// Delegate to shared implementation to avoid duplication.
return $findMatchingParent(node, predicate) as NodeType | null;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't remove $getAncestor or fix the type of $findMatchingParent per #5311 (comment)


return null;
};
export { $findMatchingParent } from 'lexical';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't work, it's used in this module so you need to import it then separately export it.

Are you running this code to test it? Pretty sure npm run ci-check would've caught this pretty quickly

while (curr !== $getRoot() && curr != null) {
if (findFn(curr)) {
return curr;
return curr as LexicalNode; // T is inferred via overload when appropriate
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cast seems redundant, and all casts are potentially unsafe so you should avoid them unless absolutely necessary

- Remove duplicate $findMatchingParent export from lexical-utils (keep only bulk re-export)
- Remove duplicate local $getAncestor function from lexical-selection (keep only exported one)
- Fix all linting and formatting issues
- TypeScript compilation now passes without errors
… $findMatchingParent

- Remove $getAncestor function definition from lexical-selection completely
- Replace all $getAncestor calls with $findMatchingParent direct calls
- Remove PR-context comment about 'Delegate to shared implementation'
- This provides full replacement rather than delegation, as requested by @etrepum

Addresses: facebook#7814 (comment)
@cnaples79
Copy link
Contributor Author

I've updated the PR and addressed your feedback @etrepum!

@cnaples79 cnaples79 marked this pull request as ready for review September 14, 2025 01:28
@etrepum etrepum added the extended-tests Run extended e2e tests on a PR label Sep 14, 2025
etrepum
etrepum previously approved these changes Sep 14, 2025
Copy link
Collaborator

@etrepum etrepum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made some suggestions for improvements to the existing code. Audited our existing usage of $getAncestor (not publicly exported, and does not depend on the implementation differences with $findMatchingParent, so it's fine to remove).

@etrepum
Copy link
Collaborator

etrepum commented Sep 15, 2025

This looks good, other than the PR title and description. Can you change these to match the repository's pull request template? It's important for how we put together the release notes.

https://raw.githubusercontent.com/facebook/lexical/refs/heads/main/.github/pull_request_template.md

@cnaples79 cnaples79 changed the title Refactor: deduplicate ancestor lookup via [lexical][lexical-selection][lexical-utils] Refactor: Consolidate ancestor lookup via Sep 15, 2025
@cnaples79 cnaples79 changed the title [lexical][lexical-selection][lexical-utils] Refactor: Consolidate ancestor lookup via [lexical][lexical-selection][lexical-utils] Refactor: Consolidate ancestor lookup via findMatchingParent Sep 15, 2025
@cnaples79
Copy link
Contributor Author

@etrepum done! Please let me know if any other changes to the PR need to be made! And thank you again for your feedback.

@etrepum etrepum added this pull request to the merge queue Sep 15, 2025
Merged via the queue into facebook:main with commit ab55dbb Sep 15, 2025
40 of 68 checks passed
This was referenced Sep 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. extended-tests Run extended e2e tests on a PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Duplicate function $findMatchingParent and $getAncestor

2 participants