UNPKG

@neurolint/cli

Version:

NeuroLint CLI - Deterministic code fixing for TypeScript, JavaScript, React, and Next.js with 8-layer architecture including Security Forensics, Next.js 16, React Compiler, and Turbopack support

137 lines (115 loc) 4.48 kB
/** * NeuroLint - Licensed under Apache License 2.0 * Copyright (c) 2025 NeuroLint * http://www.apache.org/licenses/LICENSE-2.0 */ /** * Convert ReactDOM test-utils imports to react imports (React 19) * react-dom/test-utils is removed in React 19, act is moved to react package */ function convertReactDOMTestUtils(code) { let transformedCode = code; const changes = []; // Pattern: import {act} from 'react-dom/test-utils' // Convert to: import {act} from 'react' const testUtilsPattern = /import\s*{\s*act\s*}\s*from\s*['"]react-dom\/test-utils['"]/g; let match; while ((match = testUtilsPattern.exec(code)) !== null) { const replacement = "import {act} from 'react'"; transformedCode = transformedCode.replace(match[0], replacement); changes.push({ type: 'react19-test-utils', description: 'Converted react-dom/test-utils import to react import', oldPattern: match[0], newPattern: replacement }); } // Pattern: import {act, other} from 'react-dom/test-utils' // Convert to: import {act} from 'react'; import {other} from 'react-dom/test-utils' const testUtilsWithOthersPattern = /import\s*{\s*act\s*,\s*([^}]+)\s*}\s*from\s*['"]react-dom\/test-utils['"]/g; while ((match = testUtilsWithOthersPattern.exec(code)) !== null) { const otherImports = match[1].trim(); const replacement = `import {act} from 'react';\nimport {${otherImports}} from 'react-dom/test-utils';`; transformedCode = transformedCode.replace(match[0], replacement); changes.push({ type: 'react19-test-utils-mixed', description: 'Separated act import from react-dom/test-utils to react package', oldPattern: match[0], newPattern: replacement }); } return { code: transformedCode, changes }; } /** * Apply all React 19 DOM API fixes */ function applyReact19DOMFixes(code, options = {}) { const { verbose = false } = options; let transformedCode = code; const fixes = []; const warnings = []; // 1. ReactDOM test-utils migration (NEW - Phase 1) if (transformedCode.includes('react-dom/test-utils')) { const testUtilsResult = convertReactDOMTestUtils(transformedCode); transformedCode = testUtilsResult.code; fixes.push(...testUtilsResult.changes); if (testUtilsResult.changes.length > 0 && verbose) { testUtilsResult.changes.forEach(change => { process.stdout.write(`[INFO] ${change.description}\n`); }); } } // 2. ReactDOM.render conversion if (transformedCode.includes('ReactDOM.render')) { const renderResult = convertReactDOMRender(transformedCode); transformedCode = renderResult.code; fixes.push(...renderResult.changes); if (renderResult.changes.length > 0 && verbose) { renderResult.changes.forEach(change => { process.stdout.write(`[INFO] ${change.description}\n`); }); } } // 3. ReactDOM.hydrate conversion if (transformedCode.includes('ReactDOM.hydrate')) { const hydrateResult = convertReactDOMHydrate(transformedCode); transformedCode = hydrateResult.code; fixes.push(...hydrateResult.changes); if (hydrateResult.changes.length > 0 && verbose) { hydrateResult.changes.forEach(change => { process.stdout.write(`[INFO] ${change.description}\n`); }); } } // 4. unmountComponentAtNode detection and warnings if (transformedCode.includes('unmountComponentAtNode')) { const unmountResult = convertUnmountComponentAtNode(transformedCode); warnings.push(...unmountResult.warnings); if (unmountResult.warnings.length > 0 && verbose) { unmountResult.warnings.forEach(warning => { process.stdout.write(`[WARNING] ${warning.description}\n`); process.stdout.write(`[SUGGESTION] ${warning.suggestion}\n`); }); } } // 5. findDOMNode detection and warnings if (transformedCode.includes('findDOMNode')) { const findDOMNodeWarnings = detectFindDOMNodeUsage(transformedCode); warnings.push(...findDOMNodeWarnings); if (findDOMNodeWarnings.length > 0 && verbose) { findDOMNodeWarnings.forEach(warning => { process.stdout.write(`[WARNING] ${warning.description}\n`); process.stdout.write(`[SUGGESTION] ${warning.suggestion}\n`); }); } } return { code: transformedCode, fixes, warnings, hasReact19Changes: fixes.length > 0 || warnings.length > 0 }; }