feat(net-setsimultaneousaccepts-migration): create recipe#278
feat(net-setsimultaneousaccepts-migration): create recipe#278vespa7 wants to merge 3 commits intonodejs:mainfrom
Conversation
package.json
Outdated
| "migrations", | ||
| "node.js" | ||
| ], | ||
| "node.js" ], |
There was a problem hiding this comment.
you should revert this change
AugustinMauroy
left a comment
There was a problem hiding this comment.
Missing test case for dynamic import
Yes, it’s in draft. It doesn’t need to be corrected yet. I’m waiting for a question to be resolved. |
I don't see a question. Is it something we can help with? (Could you point us to it?) |
package.json
Outdated
| "codemod", | ||
| "migrations", | ||
| "node.js" | ||
| "node.js" |
There was a problem hiding this comment.
| "node.js" | |
| "node.js" |
|
What the current state of this pr ? |
The PR is ready to be reviewed. |
AugustinMauroy
left a comment
There was a problem hiding this comment.
not too bad, good job but there are serval part of the implementation that need to be discussed
| @@ -0,0 +1,21 @@ | |||
| schema_version: "1.0" | |||
| name: "@nodejs/net-setSimultaneousAccepts-migration" | |||
There was a problem hiding this comment.
| name: "@nodejs/net-setSimultaneousAccepts-migration" | |
| name: "@nodejs/net-setSimultaneousAccepts-deprecation" |
#namingIsHard @nodejs/userland-migrations what do you think
| const linesToRemove: Range[] = []; | ||
|
|
||
| const netImportStatements = getAllNetImportStatements(root); | ||
| if (netImportStatements.length === 0) return null; |
There was a problem hiding this comment.
| if (netImportStatements.length === 0) return null; | |
| // If no import found we don't process the file | |
| if (!netImportStatements.length) return null; |
| processNetImportStatement(rootNode, statement, linesToRemove, edits); | ||
| } | ||
|
|
||
| if (edits.length === 0 && linesToRemove.length === 0) return null; |
There was a problem hiding this comment.
| if (edits.length === 0 && linesToRemove.length === 0) return null; | |
| // If there aren't any change we don't try to modify something | |
| if (!edits.length && !linesToRemove.length) return null; |
There was a problem hiding this comment.
Maybe "No changes, nothing to do" for the comment
| ...getNodeImportStatements(root, 'node:net'), | ||
| ...getNodeImportCalls(root, 'node:net'), | ||
| ...getNodeRequireCalls(root, 'node:net'), |
There was a problem hiding this comment.
| ...getNodeImportStatements(root, 'node:net'), | |
| ...getNodeImportCalls(root, 'node:net'), | |
| ...getNodeRequireCalls(root, 'node:net'), | |
| ...getNodeImportStatements(root, 'net'), | |
| ...getNodeImportCalls(root, 'net'), | |
| ...getNodeRequireCalls(root, 'net'), |
the node: isn't needed theses functions catch it
| edits: Edit[] | ||
| ): void { | ||
| const bindingPath = resolveBindingPath(statementNode, '$'); | ||
| if (!bindingPath) return; |
There was a problem hiding this comment.
| if (!bindingPath) return; | |
| if (!bindingPath) return; |
| /** | ||
| * Finds all _setSimultaneousAccepts() call expressions | ||
| */ | ||
| function findSetSimultaneousAcceptsCalls( |
There was a problem hiding this comment.
This function isn't valuable. You can "hardcode" this query in the parent functio
| const objDeclarations = rootNode.findAll({ | ||
| rule: { | ||
| any: [ | ||
| { pattern: `const ${objectName} = $_` }, |
There was a problem hiding this comment.
why ast kind/ast query can we have something stronger ?
There was a problem hiding this comment.
Mmm, I think there's probably a built-in way from ast-grep to get this without needing such a wide pattern.
There was a problem hiding this comment.
If I understand the scenario correctly, it can be rewritten as something like the code below.
It finds all variable declarations that use the name objectName.
const objDeclarations = rootNode.findAll({
kind: 'variable_declarator',
has: {
kind: 'identifier',
field: 'name',
patern: objctName,
}
})There was a problem hiding this comment.
I was thinking in something like this:
const pair = root
.root()
.findAll({ rule: { kind: "variable_declarator" } })
.filter((decl) => decl.field("name")?.text() === objectName)
.flatMap((decl) => decl.findAll({ rule: { kind: "pair" } }))
.filter((p) => p.field("key")?.text() === keyName)
.map((pair) => pair.field("value")?.text())[0]There was a problem hiding this comment.
WTF that completely crazy
There was a problem hiding this comment.
@brunocroh yeah, that's exactly the sort of thing I was thinking :)
@vespa7 that is a bit… verbose, and I suspect, overly manual (it looks a bit like a Rube Goldberg machine as code 😄). I think there is a more concise way to do it. I would try something like Bruno suggested.
@AugustinMauroy that's maybe not the most constructive way to phrase it 😅
There was a problem hiding this comment.
I think you're misunderstanding what @vespa7's comment is saying due to the thread, but the equivalent to @brunocroh's code in Alex's code is
const pair = rootNode
.findAll({ rule: { kind: "variable_declarator" } })
.filter((decl) => decl.field("name")?.text() === objectName)
If Brunos' code works it might be a bit better for that chunk, but the complete piece is substituting
const objDeclarations = rootNode.findAll({
rule: {
any: [
{ pattern: `const ${objectName} = $_` },
{ pattern: `let ${objectName} = $_` },
{ pattern: `var ${objectName} = $_` },
],
},
});
for (const objDecl of objDeclarations) {
const objectLiterals = objDecl.findAll({ rule: { kind: 'object' } });
for (const obj of objectLiterals) {
const pairs = obj.findAll({ rule: { kind: 'pair' } });
for (const pair of pairs) {
const key = pair.child(0);
if (key?.text() === propertyName) {
const rangeWithComma = expandRangeToIncludeTrailingComma(
pair.range(),
rootNode.text()
);
linesToRemove.push(rangeWithComma);
}
}
}
}
And if you got that correctly I'm not sure how the new piece looks more verbose. Following on Bruno's proposal for the whole snippet, maybe something like
const pairs = rootNode
.findAll({
rule: {
kind: "variable_declarator",
has: {
kind: "identifier",
field: "name",
pattern: objectName,
},
},
})
.flatMap((decl) =>
decl.findAll({
rule: {
kind: "pair",
has: {
field: "key",
regex: keyName,
},
},
}),
);
You can break it in the middle to give explanatory names to the steps
There was a problem hiding this comment.
Ohhhhh, sorry. I didn't realise these were different pieces of a larger whole. In that case, yes that's fine :)
There was a problem hiding this comment.
So in my example, I'm telling the rust parser to just return a variable declared with the name I need, it could be the difference between returning 4 items instead of 4 thousand.
const objDeclarations = rootNode.findAll({
kind: 'variable_declarator',
has: {
kind: 'identifier',
field: 'name',
patern: objctName, // <--- I think this is important for this scenario
}
})Returning all variable declarators from the codebase to the JavaScript context can be a performance bottleneck.
2 reasons:
- Rust is faster.
- Duplication in memory to clone all this results from rust to JavaScript side.
It would be better to filter as much as possible on the Rust side.
const pair = rootNode
.findAll({ rule: { kind: "variable_declarator" } })
.filter((decl) => decl.field("name")?.text() === objectName)This is my only concern with @vespa7’s first code proposal; the latest code from @Ceres6 looks great!
Btw, don’t take it too seriously, I’m just thinking ahead. It’s not a big issue for us yet, and we’re not tracking or benchmarking anything at the moment.
| for (const objDecl of objDeclarations) { | ||
| const objectLiterals = objDecl.findAll({ rule: { kind: 'object' } }); | ||
|
|
||
| for (const obj of objectLiterals) { | ||
| const pairs = obj.findAll({ rule: { kind: 'pair' } }); | ||
|
|
||
| for (const pair of pairs) { | ||
| const key = pair.child(0); | ||
| if (key?.text() === propertyName) { | ||
| const rangeWithComma = expandRangeToIncludeTrailingComma( | ||
| pair.range(), | ||
| rootNode.text() | ||
| ); | ||
| linesToRemove.push(rangeWithComma); | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
can we have a loop that push to an array then iterate on this array to avoid 3th level of nested loop
| varName: string, | ||
| linesToRemove: Range[] | ||
| ): void { | ||
| const varDeclarationStatements = rootNode.findAll({ |
JakobJingleheimer
left a comment
There was a problem hiding this comment.
Thanks for this! It's a good first :)
I think this could use some additional test-cases that include more things happening from node:net to ensure they don't get broken.
package.json
Outdated
| "codemod", | ||
| "migrations", | ||
| "node.js" | ||
| "node.js" |
There was a problem hiding this comment.
| "node.js" | |
| "node.js" |
| const net = require("node:net"); | ||
|
|
||
| -net._setSimultaneousAccepts(false); | ||
| const server = net.createServer(); |
There was a problem hiding this comment.
| const net = require("node:net"); | |
| -net._setSimultaneousAccepts(false); | |
| const server = net.createServer(); | |
| const net = require("node:net"); | |
| - net._setSimultaneousAccepts(false); | |
| const server = net.createServer(); |
| processNetImportStatement(rootNode, statement, linesToRemove, edits); | ||
| } | ||
|
|
||
| if (edits.length === 0 && linesToRemove.length === 0) return null; |
There was a problem hiding this comment.
Maybe "No changes, nothing to do" for the comment
| if (argKind === 'member_expression') { | ||
| handleMemberExpressionArgument(rootNode, argNode, linesToRemove); | ||
| } else if (argKind === 'identifier') { | ||
| handleIdentifierArgument(rootNode, argNode, linesToRemove); | ||
| } |
There was a problem hiding this comment.
nit: switch makes it more clear that it's inspecting the same condition for each.
| if (argKind === 'member_expression') { | |
| handleMemberExpressionArgument(rootNode, argNode, linesToRemove); | |
| } else if (argKind === 'identifier') { | |
| handleIdentifierArgument(rootNode, argNode, linesToRemove); | |
| } | |
| switch(argKind) { | |
| case 'member_expression': handleMemberExpressionArgument(rootNode, argNode, linesToRemove); break; | |
| case 'identifier': handleIdentifierArgument(rootNode, argNode, linesToRemove); break; | |
| } |
| rule: { pattern: `${objectName}.${propertyName}` }, | ||
| }); | ||
|
|
||
| // Only remove if this is the only reference |
There was a problem hiding this comment.
nit: the extra "only" is kind of cumbersome to read
| // Only remove if this is the only reference | |
| // Remove when this is the only reference |
| const objDeclarations = rootNode.findAll({ | ||
| rule: { | ||
| any: [ | ||
| { pattern: `const ${objectName} = $_` }, |
There was a problem hiding this comment.
Mmm, I think there's probably a built-in way from ast-grep to get this without needing such a wide pattern.
| return linesToRemove.length > 0 | ||
| ? removeLines(sourceCode, linesToRemove) | ||
| : sourceCode; | ||
| } No newline at end of file |
There was a problem hiding this comment.
Remember to add newlines at the end of files
Closes #173