-
-
Notifications
You must be signed in to change notification settings - Fork 17
feat(utils
): add package.json
#194
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
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this 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 adds a new utility module for handling package.json modifications using AST manipulation. It provides functions to locate scripts sections, find Node.js usage within scripts, and replace Node.js arguments.
Key changes:
- New package-json.ts utility with functions for AST-based package.json manipulation
- Comprehensive test suite covering all utility functions
- Added @ast-grep/lang-json dependency for JSON parsing support
Reviewed Changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 5 comments.
File | Description |
---|---|
utils/src/ast-grep/package-json.ts | Core utility functions for package.json AST manipulation |
utils/src/ast-grep/package-json.test.ts | Test suite covering all utility functions with edge cases |
utils/package.json | Added @ast-grep/lang-json dependency for JSON parsing |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
Co-Authored-By: Bruno Rodrigues <[email protected]>
export const replaceNodeJsArgs = (packageJsonRootNode: SgRoot, argsToValues: Record<string, string>, edits: Edit[]) => { | ||
for (const usage of getNodeJsUsage(packageJsonRootNode)) { | ||
const text = usage.text(); | ||
const bashAST = astGrep.parse("bash", text).root(); | ||
const command = bashAST.findAll({ rule: { kind: "command" } }); | ||
for (const cmd of command) { | ||
const args = cmd.findAll({ | ||
rule: { | ||
kind: "word", | ||
not: { | ||
inside: { kind: "command_name" }, | ||
}, | ||
}, | ||
}); | ||
for (const arg of args) { | ||
const oldArg = arg.text(); | ||
const newValue = argsToValues[oldArg]; | ||
if (newValue) { | ||
edits.push(arg.replace(newValue)); | ||
} | ||
} | ||
} | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think just returning the edits is a better API than passing in a variable to be mutated in this context.
Because function caller can have full control about what to do with edits.
export const replaceNodeJsArgs = (packageJsonRootNode: SgRoot, argsToValues: Record<string, string>, edits: Edit[]) => { | |
for (const usage of getNodeJsUsage(packageJsonRootNode)) { | |
const text = usage.text(); | |
const bashAST = astGrep.parse("bash", text).root(); | |
const command = bashAST.findAll({ rule: { kind: "command" } }); | |
for (const cmd of command) { | |
const args = cmd.findAll({ | |
rule: { | |
kind: "word", | |
not: { | |
inside: { kind: "command_name" }, | |
}, | |
}, | |
}); | |
for (const arg of args) { | |
const oldArg = arg.text(); | |
const newValue = argsToValues[oldArg]; | |
if (newValue) { | |
edits.push(arg.replace(newValue)); | |
} | |
} | |
} | |
} | |
}; | |
export const replaceNodeJsArgs = (packageJsonRootNode: SgRoot, argsToValues: Record<string, string>) => { | |
const edits: Edit[] = [] | |
for (const usage of getNodeJsUsage(packageJsonRootNode)) { | |
const text = usage.text(); | |
const bashAST = astGrep.parse("bash", text).root(); | |
const command = bashAST.findAll({ rule: { kind: "command" } }); | |
for (const cmd of command) { | |
const args = cmd.findAll({ | |
rule: { | |
kind: "word", | |
not: { | |
inside: { kind: "command_name" }, | |
}, | |
}, | |
}); | |
for (const arg of args) { | |
const oldArg = arg.text(); | |
const newValue = argsToValues[oldArg]; | |
if (newValue) { | |
edits.push(arg.replace(newValue)); | |
} | |
} | |
} | |
} | |
return edits | |
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
currently api will be use like that:
import { removeNodeJsArgs } from "@nodejs/codemod-utils/ast-grep/package-json";
import type { SgRoot, Edit } from "@codemod.com/jssg-types/main";
/**
* vX-to-vY
*/
export default function transform(root: SgRoot): string | null {
const rootNode = root.root();
const edits: Edit[] = [];
replaceNodeJsArgs(root, { "--experimental-feature": "--feature"}, edits);
removeNodeJsArgs(root, ["--foo"], edits);
if(!edits.length) return null;
return rootNode.commitEdits(edits);
}
With your proposal:
import { removeNodeJsArgs, replaceNodeJsArgs } from "@nodejs/codemod-utils/ast-grep/package-json";
import type { SgRoot, Edit } from "@codemod.com/jssg-types/main";
/**
* vX-to-vY
*/
export default function transform(root: SgRoot): string | null {
const rootNode = root.root();
const edits: Edit[] = [];
edits.push(
replaceNodeJsArgs(rootNode, { "--experimental-feature": "--feature"})
);
edits.push(
removeNodeJsArgs(rootNode, ["--foo"])
);
if(!edits.length) return null;
return rootNode.commitEdits(edits);
}
So In overal I didn't have any strong opinion but when you are looking at code format/style my aproach is simpler but your is more descriptive to what happened
export const removeNodeJsArgs = ( | ||
packageJsonRootNode: SgRoot, | ||
argsToRemove: string[], | ||
edits: Edit[] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
edits: Edit[] |
Description
Add utility to handle changes for
package.json
Related issues
Related to #94