Skip to content

feat(crypto-fips): add migration recipe for crypto.fips #177

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

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

max-programming
Copy link

Closes #175

….fips to crypto.getFips() and crypto.setFips()
@max-programming
Copy link
Author

Let me know if the settings.json change feels unnecessary, if that's the case, I'll remove it

Personally, I think having that helps

@avivkeller
Copy link
Member

Let me know if the settings.json change feels unnecessary, if that's the case, I'll remove it

Personally, I think having that helps

You don't have to remove it entirely, but can it be a separate PR (to keep our commits organized)?

Thank you!

@max-programming
Copy link
Author

@avivkeller Alright then. I'm gonna make another PR for another recipe where I'll include this change as a separate commit

Replaces array concatenation with generator-based iteration
to improve readability, memory usage, and performance.
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.

WOW 😵‍💫 that's an awesome first contribution. I love your usage of yield.

Thanks you for your contribution, I hope that you had enjoyed it. If you have any feedback feel it

@max-programming
Copy link
Author

Thanks @AugustinMauroy!

I'll update the suggested changes
Also there's one two more test cases that are failing which I'll add and re-request a review

…tFips/setFips

This update introduces a transformation for replacing deprecated `crypto.fips` calls with `crypto.getFips()` and `crypto.setFips()`. It includes enhancements to variable collection, import specifier updates, and destructuring adjustments to ensure proper usage of the new methods. Additionally, new test cases have been added to validate the changes.
@max-programming
Copy link
Author

@AugustinMauroy I think some functions I used can be put into utils. Ones that are used to get import specifiers and all
Let me know which ones can be re-used in other codemods

@max-programming
Copy link
Author

For the utility functions thing, I'll examine those and probably open another PR for that specifically

Comment on lines +96 to +155
function updateCryptoImportSpecifiers(root: SgRoot): Edit[] {
const edits: Edit[] = [];

const importStmts = getNodeImportStatements(root, 'crypto');

for (const stmt of importStmts) {
// import_clause contains default/namespace/named parts
const importClause = stmt.find({ rule: { kind: 'import_clause' } });
if (!importClause) continue;

// named_imports = `{ ... }`
const namedImports = importClause.find({ rule: { kind: 'named_imports' } });
if (!namedImports) continue; // nothing to edit if there is no `{ ... }`

// All specifiers inside `{ ... }`
const specifiers = namedImports.findAll({
rule: { kind: 'import_specifier' },
});
if (!specifiers || specifiers.length === 0) continue;

let hasFips = false;
let hasGet = false;
let hasSet = false;

const keepTexts: string[] = [];

for (const spec of specifiers) {
// imported name is in field "name"
const importedNameNode = spec.find({
rule: { has: { field: 'name', kind: 'identifier' } },
});
const importedName = importedNameNode
?.find({
rule: { kind: 'identifier' },
})
?.text();

if (importedName === 'fips') {
hasFips = true; // drop this one; we will add getFips/setFips instead
continue;
}
if (importedName === 'getFips') hasGet = true;
if (importedName === 'setFips') hasSet = true;

// Preserve other specifiers as-is (including aliases like `name as alias`)
keepTexts.push(spec.text());
}

if (!hasFips) continue; // only rewrite when file was importing `fips`

// Ensure both getFips and setFips are present
if (!hasGet) keepTexts.push('getFips');
if (!hasSet) keepTexts.push('setFips');

// Replace the whole `{ ... }`
edits.push(namedImports.replace(`{ ${keepTexts.join(', ')} }`));
}

return edits;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We have an utility function that removes the imported binding (utils/src/ast-grep/remove-binding).

I think it could be useful to reduce the number of lines of code. Could you please have a look at that?

Copy link
Author

@max-programming max-programming Aug 18, 2025

Choose a reason for hiding this comment

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

Will try to use the removeBinding function although I am facing some issues with named imports and renamed requires. It renames the alias after as or : instead of removing the entire thing and adding new imports for getFips and setFips

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, I created a new updateBinding utility function that handles both updates and removals. I also made the old removeBinding a wrapper around it. This new implementation might handle the scenario better, but it isn’t merged yet (#182).

If you want, you can copy and paste it to test whether it solves your scenario as well, and use it until the other branch is merged.

Comment on lines +160 to +221
function updateCryptoRequireDestructuring(root: SgRoot): Edit[] {
const edits: Edit[] = [];

const decls = getNodeRequireCalls(root, 'crypto');

for (const decl of decls) {
const objPattern = decl.find({ rule: { kind: 'object_pattern' } });
if (!objPattern) continue;

const props = objPattern.findAll({
rule: {
any: [
{ kind: 'shorthand_property_identifier_pattern' }, // `{ foo }`
{ kind: 'pair_pattern' }, // `{ foo: bar }`
],
},
});
if (!props || props.length === 0) continue;

let hasFips = false;
let hasGet = false;
let hasSet = false;

const keepTexts: string[] = [];

for (const p of props) {
if (p.kind() === 'shorthand_property_identifier_pattern') {
const name = p.text().trim();
if (name === 'fips') {
hasFips = true;
continue;
}
if (name === 'getFips') hasGet = true;
if (name === 'setFips') hasSet = true;
keepTexts.push(name);
} else {
// pair_pattern: has key + value (e.g. `fips: myFips`, `getFips: gf`)
const keyNode = p.find({ rule: { kind: 'property_identifier' } });
const key = keyNode?.text();

if (key === 'fips') {
hasFips = true; // drop any alias of fips
continue;
}
if (key === 'getFips') hasGet = true;
if (key === 'setFips') hasSet = true;

// Keep other pairs as-is (preserves aliasing/spacing nicely)
keepTexts.push(p.text().trim());
}
}

if (!hasFips) continue; // only rewrite when it actually destructured `fips`

if (!hasGet) keepTexts.push('getFips');
if (!hasSet) keepTexts.push('setFips');

edits.push(objPattern.replace(`{ ${keepTexts.join(', ')} }`));
}

return edits;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

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.

LGMT !

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.

feat: DEP0093: crypto.fips is deprecated and replaced
4 participants