Closed cheton closed 2 months ago
Open the branch in Web Editor • VS Code • Insiders
Open Preview
This pull request is automatically built and testable in CodeSandbox.
To see build info of the built libraries, click here or the icon next to each commit SHA.
All modified and coverable lines are covered by tests :white_check_mark:
Project coverage is 71.92%. Comparing base (
373161b
) to head (0c96293
).:exclamation: Current head 0c96293 differs from pull request most recent head 2b731ad
Please upload reports for the commit 2b731ad to get more accurate results.
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
/review
--pr_reviewer.extra_instructions="
In the possible issues
section, emphasize the following:
" --pr_reviewer.inline_code_comments=true --pr_reviewer.require_score_review=true
⏱️ Estimated effort to review [1-5] | 3 |
🏅 Score | 89 |
🧪 Relevant tests | Yes |
🔒 Security concerns | No |
⚡ Key issues to review | None |
/ask Are there any tests that can be improved in this PR?
/update_changelog
Changelog updates: 🔄
--extensions
and --ignore-pattern
as configurable command-line options for the codemod CLI.to commit the new content to the CHANGELOG.md file, please type: '/update_changelog --pr_update_changelog.push_changelog_changes=true'
Are there any tests that can be improved in this PR?
The tests included in this PR are generally well-structured and cover the basic functionality of the new command-line options. However, there are a few areas where the tests could be improved for better coverage and robustness:
Test for --print
Option:
--print
option is used. Adding a test to ensure that the transformed files are printed to stdout would be beneficial.Test for --jscodeshift
Option:
jscodeshift
using the --jscodeshift
flag. Adding a test for this would ensure that the flag is correctly handled.Test for Multiple --ignore-pattern
Values:
jscodeshift
would be useful.Test for Default Values:
extensions
, parser
, ignorePattern
, etc.).Edge Cases and Error Handling:
Here are some examples of how these tests could be added:
--print
Optionit('should print transformed files to stdout when --print is true', () => {
const mockSpawnSync = jest.spyOn(childProcess, 'spawnSync').mockReturnValue({ error: null });
const transform = 'react/v2.0.0/import-react-icons';
const files = ['/path/to/file1.js'];
const flags = {
extensions: 'js,ts,jsx,tsx,json',
parser: 'babel',
ignorePattern: '**/node_modules/**',
dry: false,
print: true,
jscodeshift: null,
};
run(transform, files, flags, []);
expect(mockSpawnSync).toHaveBeenCalledWith(
'node',
expect.arrayContaining([
'--print',
...files,
]),
{ stdio: 'inherit' }
);
});
--jscodeshift
Optionit('should pass advanced options to jscodeshift when --jscodeshift is provided', () => {
const mockSpawnSync = jest.spyOn(childProcess, 'spawnSync').mockReturnValue({ error: null });
const transform = 'react/v2.0.0/import-react-icons';
const files = ['/path/to/file1.js'];
const flags = {
extensions: 'js,ts,jsx,tsx,json',
parser: 'babel',
ignorePattern: '**/node_modules/**',
dry: false,
print: false,
jscodeshift: '--some-advanced-option',
};
run(transform, files, flags, []);
expect(mockSpawnSync).toHaveBeenCalledWith(
'node',
expect.arrayContaining([
'--some-advanced-option',
...files,
]),
{ stdio: 'inherit' }
);
});
--ignore-pattern
Valuesit('should handle multiple ignore patterns', () => {
const mockSpawnSync = jest.spyOn(childProcess, 'spawnSync').mockReturnValue({ error: null });
const transform = 'react/v2.0.0/import-react-icons';
const files = ['/path/to/file1.js'];
const flags = {
extensions: 'js,ts,jsx,tsx,json',
parser: 'babel',
ignorePattern: ['**/node_modules/**', '**/dist/**'],
dry: false,
print: false,
jscodeshift: null,
};
run(transform, files, flags, []);
expect(mockSpawnSync).toHaveBeenCalledWith(
'node',
expect.arrayContaining([
'--ignore-pattern',
'**/node_modules/**',
'--ignore-pattern',
'**/dist/**',
...files,
]),
{ stdio: 'inherit' }
);
});
it('should use default values for unspecified flags', () => {
const mockSpawnSync = jest.spyOn(childProcess, 'spawnSync').mockReturnValue({ error: null });
const transform = 'react/v2.0.0/import-react-icons';
const files = ['/path/to/file1.js'];
const flags = {};
run(transform, files, flags, []);
expect(mockSpawnSync).toHaveBeenCalledWith(
'node',
expect.arrayContaining([
'--extensions',
'js,ts,jsx,tsx,json',
'--parser',
'babel',
'--ignore-pattern',
'**/node_modules/**',
...files,
]),
{ stdio: 'inherit' }
);
});
Adding these tests will improve the coverage and ensure that the new command-line options are thoroughly tested.
/ask How to sanitize inputs for this PR when using childProcess.spawnSync
?
How to sanitize inputs for this PR when using childProcess.spawnSync
?
To sanitize inputs when using childProcess.spawnSync
in this PR, you should ensure that any user-provided inputs are validated and escaped to prevent command injection attacks. Here are some steps you can take to sanitize the inputs:
Validate Inputs: Ensure that the inputs conform to expected formats and types. For example, file paths should be valid paths, and flags should be within an expected set of values.
Escape Special Characters: Escape any special characters in the inputs that could be interpreted by the shell.
Use a Whitelist Approach: For flags and options, use a whitelist approach where only known and expected values are allowed.
Avoid Shell Interpretation: Use spawnSync
with an array of arguments rather than a single command string to avoid shell interpretation.
Here is an example of how you can implement these steps in the run
function:
const childProcess = require('child_process');
const fs = require('fs');
const path = require('path');
const jscodeshiftPackage = require('jscodeshift/package.json');
const jscodeshiftDirectory = path.dirname(require.resolve('jscodeshift'));
const jscodeshiftExecutable = path.join(jscodeshiftDirectory, jscodeshiftPackage.bin.jscodeshift);
function sanitizeInput(input) {
// Basic sanitization example: remove any characters that are not alphanumeric, dashes, or underscores
return input.replace(/[^a-zA-Z0-9-_.,/]/g, '');
}
function run(transform, files, flags, codemodFlags) {
const transformPaths = [
path.resolve(__dirname, './src', `${sanitizeInput(transform)}/index.js`),
path.resolve(__dirname, './src', `${sanitizeInput(transform)}.js`),
path.resolve(__dirname, './dist', `${sanitizeInput(transform)}/index.js`),
path.resolve(__dirname, './dist', `${sanitizeInput(transform)}.js`),
];
let transformPath;
let error;
for (transformPath of transformPaths) {
try {
fs.statSync(transformPath);
error = undefined;
break;
} catch (err) {
error = err;
continue;
}
}
if (error) {
if (error?.code === 'ENOENT') {
throw new Error(
`${JSON.stringify(transform)} not found. Check out ${path.resolve(__dirname, './README.md for a list of available codemods.')}`,
);
}
throw error;
}
const args = [
jscodeshiftExecutable,
'--transform',
transformPath,
...codemodFlags,
];
if (flags.extensions) {
args.push('--extensions');
args.push(sanitizeInput(flags.extensions));
}
if (flags.parser) {
args.push('--parser');
args.push(sanitizeInput(flags.parser));
}
if (flags.ignorePattern) {
const ignorePatterns = Array.isArray(flags.ignorePattern) ? flags.ignorePattern : [flags.ignorePattern];
ignorePatterns.filter(Boolean).forEach(pattern => {
args.push('--ignore-pattern', sanitizeInput(pattern));
});
}
if (flags.dry) {
args.push('--dry');
}
if (flags.print) {
args.push('--print');
}
if (flags.jscodeshift) {
args.push(sanitizeInput(flags.jscodeshift));
}
files.forEach(file => {
args.push(path.resolve(sanitizeInput(file)));
});
console.log(`Executing command: ${args.join(' ')}`);
const jscodeshiftProcess = childProcess.spawnSync('node', args, { stdio: 'inherit' });
if (jscodeshiftProcess.error) {
throw jscodeshiftProcess.error;
}
}
module.exports = run;
In this example, the sanitizeInput
function is a simple implementation that removes any characters that are not alphanumeric, dashes, underscores, commas, or slashes. This helps to prevent injection attacks by ensuring that only safe characters are passed to the command. You may need to adjust the sanitization logic based on the specific requirements and constraints of your application.
/ask The glob pattern should be allowed in your sanitizeInput
function
The glob pattern should be allowed in your sanitizeInput
function
The provided PR diff does not include a sanitizeInput
function. However, based on the context of the changes, it seems like the sanitizeInput
function should be designed to handle and allow glob patterns, especially since the new --ignore-pattern
option uses glob patterns.
To ensure glob patterns are allowed in your sanitizeInput
function, you should make sure that the function does not strip or alter characters that are commonly used in glob patterns (e.g., *
, ?
, [
, ]
, {
, }
, !
, etc.).
Here is an example of how you might implement or modify a sanitizeInput
function to allow glob patterns:
function sanitizeInput(input) {
// Assuming input is a string or an array of strings
if (Array.isArray(input)) {
return input.map(sanitizeInput);
}
// Allow common glob pattern characters and ensure the input is a string
const allowedCharacters = /^[a-zA-Z0-9_\-\/\*\?\[\]\{\}\!\.\,]+$/;
if (typeof input === 'string' && allowedCharacters.test(input)) {
return input;
}
throw new Error('Invalid input: Only alphanumeric characters, dashes, slashes, and common glob pattern characters are allowed.');
}
In this example, the allowedCharacters
regular expression includes alphanumeric characters, dashes, slashes, and common glob pattern characters. The function checks if the input matches this pattern and throws an error if it does not.
If you need more specific handling or sanitization based on your application's requirements, you can adjust the regular expression accordingly.
Is there anything remaining that's worth improving
The PR introduces several improvements and new features, but there are a few areas that could be further enhanced:
Error Handling and Logging:
run
function in codemod-main.js
logs the command being executed but does not log errors in a user-friendly way. Consider adding more descriptive error messages and logging for better debugging.if (jscodeshiftProcess.error) {
console.error('Error executing jscodeshift:', jscodeshiftProcess.error);
throw jscodeshiftProcess.error;
}
Validation of Command-Line Arguments:
sanitizeInput
function ensures that inputs contain only allowed characters, it might be beneficial to add more specific validation for each flag (e.g., ensuring extensions
are valid file extensions).function validateExtensions(extensions) {
const validExtensions = ['js', 'ts', 'jsx', 'tsx', 'json'];
extensions.split(',').forEach(ext => {
if (!validExtensions.includes(ext)) {
throw new Error(`Invalid extension: ${ext}`);
}
});
}
Unit Test Coverage:
--extensions
and --ignore-pattern
options.Example:
it('should throw an error for invalid extensions', () => {
const transform = 'react/v2.0.0/import-react-icons';
const files = ['/path/to/file1.js'];
const flags = {
extensions: 'invalidExt',
parser: 'babel',
ignorePattern: '**/node_modules/**',
dry: false,
print: false,
jscodeshift: null,
};
expect(() => run(transform, files, flags, [])).toThrowError('Invalid extension: invalidExt');
});
Documentation:
--extensions
and --ignore-pattern
).Example:
### Using `--extensions`
To specify which file extensions to transform, use the `--extensions` option:
```bash
npx @tonic-ui/codemod@latest <codemod> <path> --extensions=ts,tsx
--ignore-pattern
To ignore files matching a specific pattern, use the --ignore-pattern
option:
npx @tonic-ui/codemod@latest <codemod> <path> --ignore-pattern='**/dist/**'
Code Duplication:
sanitizeInput
function is used multiple times for different flags. Consider refactoring to reduce duplication.Example:
function addFlag(args, flagName, flagValue) {
if (flagValue) {
args.push(`--${flagName}=${sanitizeInput(flagValue)}`);
}
}
// Usage
addFlag(args, 'extensions', flags.extensions);
addFlag(args, 'parser', flags.parser);
By addressing these points, the PR can be further improved in terms of robustness, usability, and maintainability.
/ask Is there anything remaining that's worth improving
Is there anything remaining that's worth improving
The PR introduces several improvements and new features to the codemod CLI, including the addition of --extensions
and --ignore-pattern
options, and refactors the implementation. However, there are a few areas where further improvements could be made:
Documentation Updates:
README.md
file should be updated to include examples and explanations for the new --extensions
and --ignore-pattern
options. This will help users understand how to use these new features effectively.Error Handling:
Code Duplication:
sanitizeInput
function is used to validate inputs. It might be beneficial to add unit tests specifically for this function to ensure it handles all edge cases correctly.Logging and Debugging:
Test Coverage:
--extensions
and --ignore-pattern
options.Code Comments:
run
function in codemod-main.js
, can improve readability and maintainability.Consistency in Option Naming:
ignore-pattern
uses a hyphen, while jscodeshift
does not. Consider standardizing this for better consistency.Performance Considerations:
sanitizeInput
function is called frequently, consider optimizing it for performance, especially if it needs to handle large arrays of inputs.By addressing these points, the PR can be further improved to enhance usability, maintainability, and robustness.
/update_changelog
Changelog updates: 🔄
--extensions
and --ignore-pattern
as configurable command-line options for the codemod CLI.--verbose
CLI option.to commit the new content to the CHANGELOG.md file, please type: '/update_changelog --pr_update_changelog.push_changelog_changes=true'
/review --pr_reviewer.inline_code_comments=true --pr_reviewer.require_score_review=true
/review --pr_reviewer.inline_code_comments=true --pr_reviewer.require_score_review=true
Changelog
--extensions
and--ignore-pattern
as configurable command-line options for the codemod CLI.--verbose
CLI option.