-
Notifications
You must be signed in to change notification settings - Fork 4k
Migrate filterMainScriptElements to FilterMainScriptElementsVisitor #12813
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Migrate filterMainScriptElements to FilterMainScriptElementsVisitor #12813
Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
✅ PR preview is ready!
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR migrates the filterMainScriptElements method from a class method on AppNode interface to a visitor pattern implementation (FilterMainScriptElementsVisitor). This change improves code organization by externalizing tree filtering logic into a dedicated visitor class.
Key Changes
- Introduced
FilterMainScriptElementsVisitorclass that implements theAppNodeVisitorinterface - Removed
filterMainScriptElementsmethod fromAppNodeinterface,ElementNode, andBlockNode - Updated
AppRoot.filterMainScriptElementsto use the new visitor pattern
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
FilterMainScriptElementsVisitor.ts |
New visitor class implementing the filtering logic with both instance and static methods |
FilterMainScriptElementsVisitor.test.ts |
Comprehensive test suite covering visitor functionality, edge cases, and complex tree structures |
ElementNode.ts |
Removed the filterMainScriptElements method |
BlockNode.ts |
Removed the filterMainScriptElements method |
AppRoot.ts |
Refactored to use the new visitor pattern with helper method ensureBlockNode |
AppNode.interface.ts |
Removed the filterMainScriptElements method declaration from interface |
| return new BlockNode( | ||
| node.activeScriptHash, | ||
| newChildren, | ||
| node.deltaBlock, | ||
| node.scriptRunId, | ||
| node.fragmentId, | ||
| node.deltaMsgReceivedAt | ||
| ) |
Copilot
AI
Oct 18, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creating a new BlockNode instance on every visit creates unnecessary objects even when no children were filtered. Consider checking if newChildren has the same length and content as node.children before creating a new instance, returning the original node if nothing changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this will handle nested architectures here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems this is already done at line 70.
c630f8e to
11248f5
Compare
📈 Frontend coverage change detectedThe frontend unit test (vitest) coverage has increased by 0.0100%
✅ Coverage change is within normal range. |
11248f5 to
d07f3b1
Compare
b320d2a to
9f84e8e
Compare
d07f3b1 to
ba93847
Compare
9f84e8e to
770c9d0
Compare
ba93847 to
ff00c6d
Compare
770c9d0 to
5c01a6a
Compare
1788c2a to
c52b408
Compare
d81a036 to
030ba55
Compare
030ba55 to
df8e8be
Compare
c52b408 to
b9afac2
Compare
df8e8be to
ac65e88
Compare
b9afac2 to
3ecb1d2
Compare
ac65e88 to
ec71eed
Compare
3ecb1d2 to
97426f6
Compare
|
|
||
| expect(result).toBeInstanceOf(BlockNode) | ||
| expect((result as BlockNode).activeScriptHash).toBe(MAIN_SCRIPT_HASH) | ||
| expect((result as BlockNode).children).toEqual([]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[suggestion]: maybe use toHaveLength(0) instead?
|
|
||
| const nonMatchingBlock = new BlockNode( | ||
| OTHER_SCRIPT_HASH, | ||
| [nonMatchingElement], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[question] is it possible to have a matching element as child here, or no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likely not. Some magical voodoo under the hood would need to happen to cause this issue
| expect(result).toBeUndefined() | ||
| }) | ||
|
|
||
| it("filters children recursively when activeScriptHash matches", () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[suggestion] I am not sure if this description matches the test. It seems to suggest we would be filtering a child of matching block. Is it a valid scenario that the matching block would also have a non-matching child element?
Is the idea of the test that we would still filter the non-matches when there is also one thing that matches? Maybe this name would be clearer -- "filters non-matching children and keeps matches". But maybe I am missing the significance of the resursiveness?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So a block can have a non-matching element, so as long as the hash matches, it will need to continue down recursively.
The example would be
page = st.navigation(...)
with st.container(): # <- Block
page.run()the block would always match the main script hash, while the page will have different page hashes.
The other example you describe above was a non-matching block containing a matching element. I'm not sure how possible that is, BUT we just will delete the entire block since it doesn't match.
| const result = visitor.visitBlockNode(parentBlock) as BlockNode | ||
|
|
||
| expect(result).toBeInstanceOf(BlockNode) | ||
| expect(result.children).toEqual([]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[suggestion]: maybe use toHaveLength(0) instead?
| expect(result.children[0]).toBe(matchingElement) | ||
| }) | ||
|
|
||
| it("creates new visitor instance for each static call", () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[suggestion/question]: should the title be something about the static method calls being idempotent? I don't understand the importance of referencing a new visitor instance (but let me know if I am missing something).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is irrelevant really, so I removed it. It's hard to tell a new visitor instance is used.
| MAIN_SCRIPT_HASH, | ||
| [ | ||
| matchingElement, | ||
| nonMatchingElement, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] it bothers me that we are re-using the same objects here, but in practice these would be de-duped, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this is a shortcut. We could make duplicates, but the one thing about these Nodes is that they are immutable by design, so reusing them is not the a for this use case.
We do not dedupe nor would we expect references in multiple places on the tree, so I'll make the test more explicit.
| expect(result3).toBe(element1) // Should still work | ||
| }) | ||
| }) | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Perhaps test can be added to cover childrenChanged optimization:
it("returns original node when no children change", () => {
// All children match, so none are filtered
const matchingElement1 = new ElementNode(
text("element1").element,
text("element1").metadata,
"test_run_id",
MAIN_SCRIPT_HASH
)
const matchingElement2 = new ElementNode(
text("element2").element,
text("element2").metadata,
"test_run_id",
MAIN_SCRIPT_HASH
)
const blockNode = new BlockNode(
MAIN_SCRIPT_HASH,
[matchingElement1, matchingElement2],
block().deltaBlock,
"test_run_id"
)
const visitor = new FilterMainScriptElementsVisitor(MAIN_SCRIPT_HASH)
const result = visitor.visitBlockNode(blockNode)
// Critical: Should return THE SAME reference (not a new object)
// This is the childrenChanged optimization
expect(result).toBe(blockNode) // Reference equality!
expect(result).not.toBe(new BlockNode(...)) // Not a new instance
})There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call. Added with some small modifications.
ec71eed to
e8e053c
Compare
97426f6 to
96013c8
Compare
frontend/lib/src/render-tree/visitors/FilterMainScriptElementsVisitor.ts
Show resolved
Hide resolved
e8e053c to
e09e16d
Compare
96013c8 to
4e7ec2f
Compare
e09e16d to
2c656d7
Compare

Describe your changes
Migrates the filterMainScriptElements method to the FilterMainScriptElements visitor
Testing Plan
Contribution License Agreement
By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.