Skip to content

Conversation

@wmccrthy
Copy link
Contributor

No description provided.

Comment on lines 268 to 270
- upwards navigation is rly useful; in this codemod, it'd be a lot easier / more performant to query for each function call (generally), update all those that are migrated accordingly (if we have upwards nav
we can get the statement by walking up tree from the call node). unfortunately, bc some of the migrations require the whole call statement (i.e local var = call),
we cannot do this and have to query for statements for those transforms that require the whole statement context, and separately query for calls for those transforms that only need the call node

Choose a reason for hiding this comment

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

Just cut #593: I agree.

Comment on lines 272 to 275
- I'm not sure of a good way around this one, just a general performance concern, it's tricky to vary operations between node world / string world.
When operating on a node, it is not uncommon to want to re-use parts of that node in your transformed output; currently, this will require:
- for string transform: parsing the node into a string, and interpolating into your returned replacement
- for node transform: cloning node so it's mutable, updating accordingly (even more impractical, would never do this, just pointing out you CAN)

Choose a reason for hiding this comment

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

I think we should be biasing towards string transforms. Most codemods are going to be one-and-done, not multi-pass.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, writing the string transforms was much nicer for this. I guess my point here is that i'm curious if there's a better way to keep parts of existing nodes than using printer

Comment on lines 33 to 41
t:selectFromRoot(block :: any, utils.isExprIndexName)
:filter(function(node: luau.AstExprIndexName)
-- get root expression; determine if it's oldLib
return #query
.selectFromRoot(node.expression :: any, utils.isBaseToken)
:filter(function(node: luau.Token)
return node.text == libAlias
end).nodes > 0
end)

Choose a reason for hiding this comment

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

There's nothing stopping you from doing this in one step, you can do something like:

Suggested change
t:selectFromRoot(block :: any, utils.isExprIndexName)
:filter(function(node: luau.AstExprIndexName)
-- get root expression; determine if it's oldLib
return #query
.selectFromRoot(node.expression :: any, utils.isBaseToken)
:filter(function(node: luau.Token)
return node.text == libAlias
end).nodes > 0
end)
t:selectFromRoot(block :: any, function (node: luau.AstNode)
if node.tag != 'index_name' then
return nil
end
if #query.selectFromRoot(node.expression :: any, utils.isBaseToken)
:filter(function(node: luau.Token)
return node.text == libAlias
end).nodes == 0
then
return nil
end
return node
end)

Choose a reason for hiding this comment

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

Might be nice to add an exists helper that can early exit instead of doing filter and then .nodes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yea, good idea

@wmccrthy wmccrthy requested a review from hgoldstein November 21, 2025 01:57
cases: { Case },
}

return table.freeze({})

Choose a reason for hiding this comment

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

Is this required?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i believe we have to return some value, or requiring the module will error

Copy link
Contributor Author

Choose a reason for hiding this comment

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

doesn't need to be frozen tho 🤷

end
end)
end
end)

Choose a reason for hiding this comment

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

You should check out fs.walk if it's available: I think that's going to be more consistent for your use cases.

Comment on lines 37 to 53
local subcase = v.name
local subcaseBasePath = path.format(path.join(transformCasesPath, subcase))
local initialPath = path.format(path.join(subcaseBasePath, "initial.luau"))
local expectedPath = path.format(path.join(subcaseBasePath, "expected.luau"))
local resultPath = path.format(path.join(subcaseBasePath, "result.luau"))

local initialSrc = fs.readfiletostring(initialPath)

formattedTransform(initialPath, expectedPath, transformPath)

local result = fs.readfiletostring(initialPath)
fs.writestringtofile(resultPath, result)
fs.writestringtofile(initialPath, initialSrc)

local expectedResult = fs.readfiletostring(expectedPath)

assert.eq(result, expectedResult)

Choose a reason for hiding this comment

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

  1. We already spoke offline about changing the structure here.
  2. You should think about what happens if a given test doesn't conform to the expected structure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On one hand, we could use git diff via a process, but it feels kinda weird to not use an in-house solution. Maybe write a very basic diffing system that compares the output vs snapshot line-by-line?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants