Skip to content

Conversation

brunocroh
Copy link
Member

Add new updateBinding utility function and make removeBinding a wrapper for it

Summary

This PR create the new updateBinding function. That can be used to update bindings in the file

##Examples

Updating a named require/import

updateBinding(node, 'types', {newBinding: 'newTypes'}

Before:

const { types, diff } = require('node:util');
import { types, diff } = from 'node:util';

After:

const { newTypes, diff } = require('node:util');
import { newTypes, diff } = from 'node:util';

Removing a named require/import

updateBinding(node, 'types', {newBinding: undefined}

Before:

const { types, diff } = require('node:util');
import { types, diff } = from 'node:util';

After:

const { diff } = require('node:util');
import { diff } = from 'node:util';

Updating a named import with alias

updateBinding(node, 'renamedTypes', {newBinding: 'newTypes'}

Before:

import { types as renamedTypes, diff } = from 'node:util';

After:

import { newTypes as renamedTypes, diff } = from 'node:util';

Changes Made

  • Created updateBinding function in /utils/src/ast-grep/update-binding.ts
  • Refactored existing remove-binding function to use the new updateBinding internally

Copy link
Member

@AugustinMauroy AugustinMauroy left a comment

Choose a reason for hiding this comment

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

LGTM ! thanks for that

@brunocroh brunocroh force-pushed the feat/update-binding-utility branch from 2b2978d to 164d78c Compare August 22, 2025 18:05
@AugustinMauroy
Copy link
Member

wait these function can support dynamic import

@brunocroh
Copy link
Member Author

brunocroh commented Aug 31, 2025

wait these function can support dynamic import

First, we need to merge #189. Once that is merged, we should update resolveBindingPath and adjust it if needed.

This utility function is called with the result of getNodeImportCalls and resolveBindingPath, e.g.:

const importStatement = getNodeImportCalls(node, "util")
const binding = resolveBindingPath(importStatement, "$.diff")
const edits = updateBinding(importStatement, binding, {newBinding: "newDiff"})

@AugustinMauroy AugustinMauroy added the blocked:upstream Depends on another PR or external change/fix label Aug 31, 2025
@AugustinMauroy AugustinMauroy requested a review from a team August 31, 2025 19:48
@JakobJingleheimer
Copy link
Member

@brunocroh this is no-longer blocked, right?

@JakobJingleheimer JakobJingleheimer added the awaiting author Reviewer has requested something from the author label Sep 23, 2025
@brunocroh
Copy link
Member Author

@brunocroh this is no-longer blocked, right?

Yep, no more blockers. I'm just finishing a PR for #191, and then I'll come back to this one.

@JakobJingleheimer JakobJingleheimer removed the awaiting author Reviewer has requested something from the author label Sep 23, 2025
@JakobJingleheimer
Copy link
Member

@brunocroh could you please undo the formatting changes? It's hugely bloating the recent diff

@brunocroh
Copy link
Member Author

@brunocroh could you please undo the formatting changes? It's hugely bloating the recent diff

Done, Jakob. This one is now ready to be reviewed/merged.

I’m also covering the scenarios handled in #193.
After merge, that one can be updated to something like the example below.

before

// Process transformations in order:
// 1. CommonJS require statements
processStatements(root, edits, "require");
// 2. ESM import statements
processStatements(root, edits, "import-static");
// 3. Dynamic import statements
processStatements(root, edits, "import-dynamic");

after

const binding = resolveBindingPath(node, "SlowBuffer");
updateBinding(node, {
  old: binding,
  new: "Buffer"
})

Copy link
Member

@AugustinMauroy AugustinMauroy left a comment

Choose a reason for hiding this comment

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

what if wee need to change the import itself.
for example
import aFonction from 'node:bar'; have to be updated to import anOtherFunction from 'node:quez'; ?

options?: UpdateBindingOptions,
): UpdateBindingReturnType {
const nodeKind = node.kind().toString();

Copy link
Member

Choose a reason for hiding this comment

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

if (kind !=== "") {throws error ...} we can add check to ensure good node is passed.

Copy link
Member Author

Choose a reason for hiding this comment

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

I do not have sure if I get it, some lines below we check the node kind already, I think if this is a kind that we not handle here, it just will be return undefined at the end, could you please give me more context?

	if (requireKinds.includes(nodeKind)) {
		return handleNamedRequireBindings(node, options);
	}

	if (importKinds.includes(nodeKind)) {
		return handleNamedImportBindings(node, options);
	}

Copy link
Member

Choose a reason for hiding this comment

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

why not:

	if (requireKinds.includes(nodeKind)) {
		return handleNamedRequireBindings(node, options);
	} else if (importKinds.includes(nodeKind)) {
		return handleNamedImportBindings(node, options);
	} else {
		throws new Erro(`Invalid node kind [${nodeKind}]`)
	}

Copy link
Member Author

Choose a reason for hiding this comment

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

It today already returns undefined if not possible update, we throw an error it forces to who is using this utility to put a try/catch on it

Example

actual code

const result = updateBinding(invalidNode, {old, new: "test"})
if (result != undefined) {
  // do something 
}

What it forces to be

let isPossibleUpdate = true

try {
const result = updateBinding(invalidNode, {old, new: "test"})

// result is undefined so update var to false
if (result === undefined) {
 isPossibleUpdate = false
}
}catch(e) {
 // error happen, so update var to false
 isPossibleUpdate = false
}

// now after if we can write the logic without need to duplicated it
if (isPossibleUpdate) {
}

let me know what you think

Copy link
Member

Choose a reason for hiding this comment

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

what if invalid node is pass as arg it's an error from dev so if we throws a message it's will allow us to have better DX.
also if there are any change we should return null so we know everything work but nothing happened

Co-authored-by: Augustin Mauroy <[email protected]>
@brunocroh brunocroh requested a review from a team October 6, 2025 17:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked:upstream Depends on another PR or external change/fix
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants