UNPKG

move-prop-types

Version:

"Help quickly replace the prop type checker in older react projects to map to prop-types"

165 lines 6.6 kB
/** * AST-based helper functions for PropTypes transformation * This is an alternative to the regex-based helper.ts with better accuracy and reliability */ import chalk from 'chalk'; import { stdout } from 'process'; import { lstatSync, readdir, readFile, writeFile } from 'fs'; import { exec } from 'child_process'; import { promisify } from 'util'; import { transformWithAST, needsTransformation } from './ast-transformer.js'; import { fileEncoding } from './constants.js'; const execAsync = promisify(exec); const readFileAsync = promisify(readFile); const writeFileAsync = promisify(writeFile); const readdirAsync = promisify(readdir); /** * Install prop-types package (same as original) */ export const installPackage = async () => { console.log(''); try { // Check if the package is installed in the project await import('prop-types'); console.log(`${chalk.cyan.underline.bold('prop-types')} is already installed in your project`); } catch { console.log('Installing prop-types to your project'); try { const { stdout: installOutput, stderr } = await execAsync('pnpm add prop-types'); if (stderr) { console.log(`stderr: ${stderr}`); console.log(''); return; } // the *entire* stdout (buffered) console.log(`${chalk.hex('#FF6347').bold('Installation underway')}`); console.log(installOutput); console.log(`${chalk.cyan.underline.bold('prop-types')} is now installed`); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error('Error installing prop-types:', errorMessage); } } }; /** * Write file with AST-based PropTypes conversion */ const writeFileAsyncAST = async (fileAndPath) => { try { const data = await readFileAsync(fileAndPath, fileEncoding); const dataString = data.toString(); // Use AST to check if transformation is needed if (!needsTransformation(dataString, fileAndPath)) { return; } // Transform using AST const result = transformWithAST(dataString, fileAndPath); if (result.modified && result.code) { await writeFileAsync(fileAndPath, result.code, fileEncoding); console.log(`${chalk.magenta.italic(fileAndPath)} just got ${chalk.green('updated')} with AST!`); } } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`Error processing file ${fileAndPath}:`, errorMessage); } }; /** * Update a single file using AST transformation */ export const updateFile = async (cmd, fileAndPath) => { if (!fileAndPath) { console.error('No file path provided'); return; } let targetPath = fileAndPath; // Handle file extension validation if (/[.]/.exec(targetPath)) { if (!/\S+\.(jsx?|tsx?)$/.test(targetPath)) { console.log(`Skipping ${targetPath} - not a .js, .jsx, .ts, or .tsx file`); return; } } else { // Try to find the file with .js, .jsx, .ts, or .tsx extension const fs = await import('fs'); const statAsync = promisify(fs.stat); const extensions = ['.js', '.jsx', '.ts', '.tsx']; let found = false; for (const ext of extensions) { try { await statAsync(`${targetPath}${ext}`); targetPath = `${targetPath}${ext}`; found = true; break; } catch { // Continue to next extension } } if (!found) { console.log(`${chalk.magenta.italic(targetPath)} doesn't ${chalk.red.inverse('seem to exist in the given path')} with extensions .js, .jsx, .ts, or .tsx`); return; } } await writeFileAsyncAST(targetPath); }; /** * Update all files in a folder recursively using AST transformation */ export const updateFolder = async (cmd, folderName) => { console.log(''); try { const files = await readdirAsync(folderName); const folderInFolder = files.filter((source) => lstatSync(`${folderName}/${source}`).isDirectory()); // Process subdirectories recursively for (const folder of folderInFolder) { await updateFolder('updateFolder', `${folderName}/${folder}`); } const filesInFolder = files.filter((source) => !lstatSync(`${folderName}/${source}`).isDirectory()); // Process files in current directory (filter for supported file types) const supportedFiles = filesInFolder.filter(file => /\.(jsx?|tsx?)$/.test(file)); for (const file of supportedFiles) { await updateFile('updateFolder', `${folderName}/${file}`); } console.log(''); console.log(`folder ${chalk.underline.yellowBright(folderName)} and js/jsx/ts/tsx files inside are now ${chalk.greenBright('ready')} with AST!`); stdout.write('\\x1b[2J'); stdout.write('\\x1b[0f'); console.log(`Your folder and files have been updated with AST transformation. Thank you for using ${chalk.yellowBright('move-prop-types')}`); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`Error processing folder ${folderName}:`, errorMessage); } }; /** * Display help examples (same as original) */ export const helpExamples = () => { return ` Examples (AST-based transformation): $ move-prop-types --help for info $ move-prop-types -P ../dir1/dir2/filename.[js|jsx|ts|tsx] - This will run AST-based replace only on the given file. $ move-prop-types -F ../dir1/dir2 - This will run the AST-based update for all the files inside the given directory $ move-prop-types -I -F ../dir1/dir2 - This will install prop-types to dependencies and run the AST-based update for all the files inside the given directory `; }; /** * Find matching value in array (same as original) */ export const findMatch = (givenValue, setToMatch) => { if (!Array.isArray(givenValue)) { return ''; } let index = 0; givenValue.filter((val) => { if (val === setToMatch[0] || val === setToMatch[1]) { index = givenValue.indexOf(val) + 1; } }); return index && index < givenValue.length ? givenValue[index] || '' : ''; }; //# sourceMappingURL=ast-helper.js.map