@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
363 lines (316 loc) • 11.8 kB
JavaScript
/**
* NeuroLint - Licensed under Apache License 2.0
* Copyright (c) 2025 NeuroLint
* http://www.apache.org/licenses/LICENSE-2.0
*/
/**
* Turbopack Migration Assistant
*
* Detects Webpack-specific configurations that won't work with Turbopack
* Provides migration guidance and optimization suggestions
*/
const fs = require('fs').promises;
const path = require('path');
class TurbopackMigrationAssistant {
constructor(options = {}) {
this.verbose = options.verbose || false;
this.projectPath = options.projectPath || process.cwd();
this.issues = [];
this.suggestions = [];
}
log(message, level = 'info') {
if (this.verbose || level === 'error' || level === 'warning') {
const prefix = level === 'error' ? '[ERROR]' :
level === 'warning' ? '[WARNING]' :
level === 'success' ? '[SUCCESS]' : '[INFO]';
console.log(`${prefix} ${message}`);
}
}
/**
* Main migration check
*/
async analyze() {
this.log('Analyzing project for Turbopack compatibility...', 'info');
try {
await this.checkNextConfig();
await this.checkWebpackConfig();
await this.checkBabelConfig();
await this.checkCustomLoaders();
await this.checkPlugins();
this.generateRecommendations();
this.printReport();
return {
compatible: this.issues.length === 0,
issues: this.issues,
suggestions: this.suggestions
};
} catch (error) {
this.log(`Analysis failed: ${error.message}`, 'error');
throw error;
}
}
/**
* Check next.config for Webpack-specific settings
*/
async checkNextConfig() {
const configPaths = [
path.join(this.projectPath, 'next.config.js'),
path.join(this.projectPath, 'next.config.mjs'),
path.join(this.projectPath, 'next.config.ts')
];
for (const configPath of configPaths) {
try {
const exists = await fs.access(configPath).then(() => true).catch(() => false);
if (!exists) continue;
const content = await fs.readFile(configPath, 'utf8');
// Check for webpack function customization
if (content.includes('webpack:') || content.includes('webpack(')) {
this.issues.push({
severity: 'high',
file: path.basename(configPath),
issue: 'Custom webpack configuration detected',
description: 'Turbopack does not support custom webpack configurations',
fix: 'Either remove webpack customizations or use --webpack flag to keep using Webpack'
});
}
// Check for Webpack plugins
if (content.includes('new webpack.') || content.includes('webpack.DefinePlugin')) {
this.issues.push({
severity: 'high',
file: path.basename(configPath),
issue: 'Webpack plugins detected',
description: 'Turbopack has its own plugin system and doesn\'t support Webpack plugins',
fix: 'Migrate to Turbopack-compatible alternatives or use --webpack flag'
});
}
// Check for module aliases (these work but might need adjustment)
if (content.includes('resolve.alias') || content.includes('resolve: {')) {
this.suggestions.push({
type: 'info',
message: 'Webpack resolve.alias detected',
recommendation: 'Turbopack supports path aliases via tsconfig.json or jsconfig.json. Consider migrating.'
});
}
// Check if already using Turbopack filesystem caching
if (!content.includes('turbopackFileSystemCacheForDev')) {
this.suggestions.push({
type: 'optimization',
message: 'Enable Turbopack filesystem caching',
recommendation: 'Add experimental.turbopackFileSystemCacheForDev: true for faster rebuilds',
code: `experimental: {\n turbopackFileSystemCacheForDev: true,\n}`
});
}
return;
} catch (error) {
continue;
}
}
}
/**
* Check for separate webpack.config.js
*/
async checkWebpackConfig() {
const webpackConfigPath = path.join(this.projectPath, 'webpack.config.js');
const exists = await fs.access(webpackConfigPath).then(() => true).catch(() => false);
if (exists) {
this.issues.push({
severity: 'high',
file: 'webpack.config.js',
issue: 'Standalone webpack configuration detected',
description: 'Turbopack cannot use webpack.config.js files',
fix: 'Migrate webpack configuration to next.config.js or remove if not needed with Turbopack'
});
}
}
/**
* Check Babel configuration
*/
async checkBabelConfig() {
const babelPaths = [
path.join(this.projectPath, '.babelrc'),
path.join(this.projectPath, '.babelrc.json'),
path.join(this.projectPath, '.babelrc.js'),
path.join(this.projectPath, 'babel.config.js')
];
for (const babelPath of babelPaths) {
const exists = await fs.access(babelPath).then(() => true).catch(() => false);
if (exists) {
this.suggestions.push({
type: 'info',
message: `Babel configuration found: ${path.basename(babelPath)}`,
recommendation: 'Turbopack automatically picks up Babel configs, but SWC is preferred for better performance. Consider migrating from Babel to SWC.'
});
return;
}
}
// No Babel config - that's good for Turbopack
this.suggestions.push({
type: 'success',
message: 'No Babel configuration detected',
recommendation: 'Good! Turbopack uses SWC by default which is faster than Babel.'
});
}
/**
* Check for custom loaders
*/
async checkCustomLoaders() {
const loaderPatterns = [
'style-loader',
'css-loader',
'sass-loader',
'less-loader',
'url-loader',
'file-loader',
'raw-loader'
];
const packageJsonPath = path.join(this.projectPath, 'package.json');
try {
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
const allDeps = {
...packageJson.dependencies,
...packageJson.devDependencies
};
const foundLoaders = loaderPatterns.filter(loader => allDeps[loader]);
if (foundLoaders.length > 0) {
this.issues.push({
severity: 'medium',
file: 'package.json',
issue: `Webpack loaders detected: ${foundLoaders.join(', ')}`,
description: 'Turbopack has built-in support for CSS, Sass, and file imports',
fix: 'Remove unused loaders if relying on Turbopack built-in features'
});
}
} catch (error) {
// package.json not found or invalid
}
}
/**
* Check for Webpack plugins in dependencies
*/
async checkPlugins() {
const pluginPatterns = [
'html-webpack-plugin',
'mini-css-extract-plugin',
'copy-webpack-plugin',
'webpack-bundle-analyzer'
];
const packageJsonPath = path.join(this.projectPath, 'package.json');
try {
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
const allDeps = {
...packageJson.dependencies,
...packageJson.devDependencies
};
const foundPlugins = pluginPatterns.filter(plugin => allDeps[plugin]);
if (foundPlugins.length > 0) {
this.issues.push({
severity: 'medium',
file: 'package.json',
issue: `Webpack plugins detected: ${foundPlugins.join(', ')}`,
description: 'These plugins won\'t work with Turbopack',
fix: 'Remove if not using Webpack, or continue using --webpack flag'
});
}
} catch (error) {
// package.json not found or invalid
}
}
/**
* Generate recommendations based on findings
*/
generateRecommendations() {
const highSeverityIssues = this.issues.filter(i => i.severity === 'high').length;
const mediumSeverityIssues = this.issues.filter(i => i.severity === 'medium').length;
if (highSeverityIssues > 0) {
this.suggestions.unshift({
type: 'critical',
message: `${highSeverityIssues} critical compatibility issues found`,
recommendation: 'Consider keeping Webpack for now using: next dev --webpack\nOr migrate away from custom Webpack configurations to use Turbopack'
});
} else if (mediumSeverityIssues > 0) {
this.suggestions.unshift({
type: 'warning',
message: `${mediumSeverityIssues} minor issues found`,
recommendation: 'Most issues can be resolved by cleaning up unused dependencies'
});
} else {
this.suggestions.unshift({
type: 'success',
message: 'Project is Turbopack-ready!',
recommendation: 'No blocking issues found. You can safely use Turbopack for faster builds.'
});
}
// Always suggest enabling filesystem caching
this.suggestions.push({
type: 'optimization',
message: 'Enable Turbopack optimizations',
recommendation: 'For large projects, enable filesystem caching for even faster startup times',
implementation: 'Add to next.config.js:\nexperimental: {\n turbopackFileSystemCacheForDev: true,\n}'
});
}
/**
* Print formatted report
*/
printReport() {
console.log('\n' + '='.repeat(60));
console.log(' Turbopack Migration Analysis');
console.log('='.repeat(60) + '\n');
// Issues section
if (this.issues.length > 0) {
console.log(`Found ${this.issues.length} compatibility issues:\n`);
this.issues.forEach((issue, i) => {
const severity = issue.severity === 'high' ? 'HIGH' :
issue.severity === 'medium' ? 'MEDIUM' : 'LOW';
console.log(`${i + 1}. [${severity}] ${issue.issue}`);
console.log(` File: ${issue.file}`);
console.log(` ${issue.description}`);
console.log(` Fix: ${issue.fix}`);
console.log('');
});
} else {
console.log('(no issues found) No compatibility issues found!\n');
}
// Suggestions section
if (this.suggestions.length > 0) {
console.log('Recommendations:\n');
this.suggestions.forEach((sug, i) => {
const prefix = sug.type === 'critical' ? '[CRITICAL]' :
sug.type === 'warning' ? '[WARNING]' :
sug.type === 'success' ? '[SUCCESS]' :
sug.type === 'optimization' ? '[OPTIMIZATION]' : '[INFO]';
console.log(`${prefix} ${sug.message}`);
console.log(` ${sug.recommendation}`);
if (sug.code || sug.implementation) {
console.log('\n ' + (sug.code || sug.implementation).split('\n').join('\n '));
}
console.log('');
});
}
// Migration commands
console.log('Migration Commands:\n');
console.log('# Use Turbopack (Next.js 16 default):');
console.log(' next dev');
console.log('\n# Continue using Webpack:');
console.log(' next dev --webpack');
console.log(' next build --webpack');
console.log('\n# Enable Turbopack filesystem caching:');
console.log(' # Add to next.config.js:');
console.log(' experimental: { turbopackFileSystemCacheForDev: true }');
console.log('\n' + '='.repeat(60) + '\n');
}
/**
* Generate migration config
*/
async generateMigrationConfig() {
const config = {
turbopackReady: this.issues.filter(i => i.severity === 'high').length === 0,
recommendedApproach: this.issues.length === 0 ? 'turbopack' : 'webpack',
optimizations: {
filesystemCaching: true
}
};
return config;
}
}
module.exports = TurbopackMigrationAssistant;