Skip to content

Conversation

@AugustinMauroy
Copy link
Member

@AugustinMauroy AugustinMauroy commented Oct 30, 2025

@AugustinMauroy
Copy link
Member Author

I need review on test cases so I can finish/clean the implementation

@AugustinMauroy AugustinMauroy requested review from a team and Copilot October 31, 2025 14:09
@AugustinMauroy AugustinMauroy added awaiting reviewer Author has responded and needs action from the reviewer dep:v24 Migration handles deprecation introduced in node v24 dep:EoL Migration handles deprecation introduced in an EoL version of node labels Oct 31, 2025
Copy link
Contributor

Copilot AI left a 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 introduces a comprehensive codemod recipe to migrate deprecated Node.js timers APIs to modern timer functions. The codemod replaces timers.enroll(), timers.unenroll(), timers.active(), and timers._unrefActive() with standard setTimeout(), clearTimeout(), and Timer#unref() constructs, addressing Node.js deprecations DEP0095, DEP0096, DEP0126, and DEP0127.

Key changes include:

  • New utility functions for GCD calculation and indent detection in the codemod-utils package
  • Five transformation modules that handle different deprecated APIs
  • Comprehensive test suite covering various import/require patterns
  • Automated cleanup of unused imports after transformations

Reviewed Changes

Copilot reviewed 45 out of 46 changed files in this pull request and generated 107 comments.

Show a summary per file
File Description
utils/src/math.ts Implements GCD function to calculate indentation units
utils/src/math.test.ts Tests for GCD function including edge cases
utils/src/ast-grep/indent.ts Detects indentation style and retrieves line indentation
utils/src/ast-grep/indent.test.ts Tests for indentation detection utilities
utils/src/ast-grep/general.ts Helper functions for AST manipulation
utils/src/ast-grep/general.test.ts Tests for AST manipulation helpers
recipes/timers-deprecations/src/*.ts Five transformation modules for each deprecated API
recipes/timers-deprecations/tests/** Comprehensive test cases for all transformations
recipes/timers-deprecations/workflow.yaml Workflow configuration for codemod execution
recipes/timers-deprecations/package.json Package configuration and test scripts
recipes/timers-deprecations/codemod.yaml Codemod metadata and registry configuration
recipes/timers-deprecations/README.md Documentation with examples and caveats

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@AugustinMauroy AugustinMauroy linked an issue Nov 1, 2025 that may be closed by this pull request
Copy link
Member

@JakobJingleheimer JakobJingleheimer left a comment

Choose a reason for hiding this comment

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

Thanks for this and sorry it's taken me a quite a while to get to it. It's been a helluva month :(

@JakobJingleheimer
Copy link
Member

@AugustinMauroy I tried to review the rest of this, but the browser tab crashes trying to load it (it's big, but I didn't think it was that big). Would it be possible to break this into smaller pieces?

@AugustinMauroy
Copy link
Member Author

@AugustinMauroy I tried to review the rest of this, but the browser tab crashes trying to load it (it's big, but I didn't think it was that big). Would it be possible to break this into smaller pieces?

I already do that. I just sub step for each deprecation + cleanup import

@AugustinMauroy AugustinMauroy requested a review from a team December 15, 2025 19:32
Copy link
Member

@JakobJingleheimer JakobJingleheimer left a comment

Choose a reason for hiding this comment

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

Thanks for this! Smaller PRs though please 🫠

I didn't review all the test files, but the implementation looks good! I don't understand how DEP0095 possibly works, but it has test coverage, so it obviously does 🤪

@JakobJingleheimer JakobJingleheimer removed the awaiting reviewer Author has responded and needs action from the reviewer label Jan 1, 2026
@AugustinMauroy AugustinMauroy added the awaiting author Reviewer has requested something from the author label Jan 9, 2026
@AugustinMauroy AugustinMauroy added awaiting reviewer Author has responded and needs action from the reviewer and removed awaiting author Reviewer has requested something from the author labels Jan 17, 2026
Comment on lines +39 to +47
const matches = rootNode.findAll({
rule: {
any: [
{ pattern: `${bindingPath}($RESOURCE)` },
{ pattern: `${bindingPath}($RESOURCE, $$$REST)` },
],
},
});

Copy link
Member

Choose a reason for hiding this comment

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

What do you think about having just one rule that always returns all arguments, and then we just take the first one, since that’s the only one that matters?

Suggested change
const matches = rootNode.findAll({
rule: {
any: [
{ pattern: `${bindingPath}($RESOURCE)` },
{ pattern: `${bindingPath}($RESOURCE, $$$REST)` },
],
},
});
const args = rootNode.findAll({
rule: {
any: [
{ pattern: `${bindingPath}($$$ARGS)` },
],
},
});
firstArg = args[0]

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's will need to write something like that:

const argsNode = match.field('arguments');
			if (!argsNode) continue;
			const argNodes = argsNode
				.children()
				.filter((c) => ![',', '(', ')'].includes(c.kind()));
			const resourceNode = argNodes.length ? argNodes[0] : null;

Which isn't better

Comment on lines +38 to +39
{ pattern: `${bindingPath}($RESOURCE)` },
{ pattern: `${bindingPath}($RESOURCE, $$$REST)` },
Copy link
Member

Choose a reason for hiding this comment

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

ditto: What do you think about having just one rule that always returns all arguments, and then we just take the first one, since that’s the only one that matters?

Comment on lines +42 to +43
{ pattern: `${bindingPath}($RESOURCE)` },
{ pattern: `${bindingPath}($RESOURCE, $$$REST)` },
Copy link
Member

Choose a reason for hiding this comment

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

ditto: What do you think about having just one rule that always returns all arguments, and then we just take the first one, since that’s the only one that matters?

@brunocroh
Copy link
Member

All the comments I added are minor and probably just nits, so feel free to add whatever you think is relevant.

Awesome work mate!

@JakobJingleheimer
Copy link
Member

I think we can proceed with this as-is, but I would reaaaaaalllly prefer if this was broken into separate migrations with a wrapper. The OP even highlights that these things are separate:

I think that is literally the structure this should have:

  • v24 timer deprecations wrapper
    • timers.enroll() migration
    • timers.unenroll() migration
    • timers.active() migration
    • timers._unrefActive() migration

@AugustinMauroy
Copy link
Member Author

I think we can proceed with this as-is, but I would reaaaaaalllly prefer if this was broken into separate migrations with a wrapper. The OP even highlights that these things are separate:

I think that is literally the structure this should have:

  • v24 timer deprecations wrapper

    • timers.enroll() migration
    • timers.unenroll() migration
    • timers.active() migration
    • timers._unrefActive() migration

I think you had right ... But I'm lazy I dunno how to name theses codemod.

What do we Win to split codemod into small codemod since we use steps ?

Co-Authored-By: Bruno Rodrigues <[email protected]>
@JakobJingleheimer
Copy link
Member

But I'm lazy

Fair. Me too

how to name theses codemod

What it says on the tin: timers-enroll-to-… etc

What do we Win to split codemod into small codemod since we use steps?

Users may not need or want to migrate AllTheThings. They may want to migrate just a subset. If they want AllTheThings, run the wrapper; if just A, B, C, run A, B, C.

It also makes the PR more digestable. It's currently 1.75K LoC 😱

@AugustinMauroy
Copy link
Member Author

Users may not need or want to migrate AllTheThings. They may want to migrate just a subset. If they want AllTheThings, run the wrapper; if just A, B, C, run A, B, C.

Oh make sense !

It also makes the PR more digestable. It's currently 1.75K LoC 😱

In future I will not do that again sorry 😅 but this time if I split into multiple pr it's will take more time to ship.
and if I modify this pr to use multiple codemod it's will increase the amount of code

@JakobJingleheimer
Copy link
Member

if I split into multiple pr it's will take more time to ship

Yes 😥 I think I'll have a couple hours this weekend to help.

if I modify this pr to use multiple codemod it's will increase the amount of code

I think only slightly. If there is commonality, hoist it into a helper within the wrapper and share it 🙂

@AugustinMauroy
Copy link
Member Author

Yes 😥 I think I'll have a couple hours this weekend to help.

If you can do it. But me I'm going to be off so the only action that I will be able to do is to approve pr.

@JakobJingleheimer
Copy link
Member

I'm away for a week starting Tuesday.

Btw we could also suppose there is a really bad bug in one of these. If they're all together, users can't use any of them; but if they're separate, users who need any of the 3 without the hypothetical bug can use them just fine instead of being stuck.

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

Labels

awaiting reviewer Author has responded and needs action from the reviewer dep:EoL Migration handles deprecation introduced in an EoL version of node dep:v24 Migration handles deprecation introduced in node v24

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DEP0127: timers._unrefActive() DEP0126: timers.active() DEP0096: timers.unenroll() DEP0095: timers.enroll() feat: handle timers deprecation

4 participants