local-leetcode-trainer
Version:
A complete local LeetCode practice environment with multi-language support - use your IDE, collaborate with AI, submit with confidence
184 lines (152 loc) ⢠5.59 kB
JavaScript
const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');
const { getCurrentLanguage, getLanguageConfig } = require('./config.js');
// Function to extract LeetCode link from a problem file
function extractLeetCodeLink(filePath) {
try {
const content = fs.readFileSync(filePath, 'utf8');
const linkMatch = content.match(/Link:\s*(https:\/\/leetcode\.com\/problems\/[^\/\s]+\/)/);
return linkMatch ? linkMatch[1] : null;
} catch (error) {
return null;
}
}
// Helper function to check if file is a problem file
function isProblemFile(filename) {
return filename.endsWith('.js') || filename.endsWith('.py') || filename.endsWith('.java') || filename.endsWith('.cpp');
}
// Function to find all problem files recursively
function findProblemFiles(dir = process.cwd()) {
const files = [];
function scanDir(currentDir) {
const items = fs.readdirSync(currentDir);
for (const item of items) {
const fullPath = path.join(currentDir, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory() && !item.startsWith('.') && item !== 'node_modules' && item !== 'scripts') {
scanDir(fullPath);
} else if (isProblemFile(item) && !item.includes('test') && !['test-runner.js', 'open-problem.js', 'challenge.js', 'complete.js', 'config.js'].includes(item)) {
files.push(fullPath);
}
}
}
scanDir(dir);
return files;
}
// Function to open URL in default browser
function openInBrowser(url) {
const platform = process.platform;
let command;
if (platform === 'darwin') {
command = `open "${url}"`;
} else if (platform === 'win32') {
command = `start "${url}"`;
} else {
command = `xdg-open "${url}"`;
}
exec(command, (error) => {
if (error) {
console.log(`ā Failed to open browser: ${error.message}`);
console.log(`š Please open this link manually: ${url}`);
} else {
console.log(`š Opened LeetCode problem in your browser!`);
}
});
}
// Function to resolve problem path
function resolveProblemPath(input) {
const projectRoot = process.cwd();
const language = getCurrentLanguage();
const langConfig = getLanguageConfig(language);
// If it's already a full path with extension and exists, use it
if (input.includes('.')) {
const fullInputPath = path.join(projectRoot, input);
if (fs.existsSync(fullInputPath)) {
return fullInputPath;
}
}
// Try to construct the full path: difficulty/problem-name/problem-name.ext
const parts = input.split('/');
if (parts.length === 2) {
const [difficulty, problemName] = parts;
// First try active folder
const activePath = path.join(projectRoot, difficulty, problemName, `${problemName}${langConfig.extension}`);
if (fs.existsSync(activePath)) {
return activePath;
}
// Then try completed folder within difficulty
const completedPath = path.join(projectRoot, difficulty, 'completed', problemName, `${problemName}${langConfig.extension}`);
if (fs.existsSync(completedPath)) {
return completedPath;
}
}
return null;
}
// Main function
function main() {
const args = process.argv.slice(2);
let input = args[0];
if (!input) {
// Find all problem files and show them
const problemFiles = findProblemFiles();
if (problemFiles.length === 0) {
console.log('ā No problem files found.');
process.exit(1);
}
console.log('š Available problems:');
// Separate active and completed problems
const activeProblems = [];
const completedProblems = [];
problemFiles.forEach(file => {
const projectRoot = process.cwd();
const relativePath = path.relative(projectRoot, file);
const shortPath = relativePath.replace(/\/[^\/]+\.js$/, '');
if (shortPath.includes('/completed/')) {
completedProblems.push({ file, shortPath });
} else {
activeProblems.push({ file, shortPath });
}
});
if (activeProblems.length > 0) {
console.log('š„ Active Problems:');
activeProblems.forEach((item, index) => {
const link = extractLeetCodeLink(item.file);
const status = link ? 'š' : 'ā';
console.log(` ${index + 1}. ${status} ${item.shortPath}`);
});
console.log('');
}
if (completedProblems.length > 0) {
console.log('ā
Completed Problems:');
completedProblems.forEach((item, index) => {
const link = extractLeetCodeLink(item.file);
const status = link ? 'š' : 'ā';
console.log(` ${index + 1}. ${status} ${item.shortPath}`);
});
console.log('');
}
console.log('\nš” Usage:');
console.log(' yarn open <difficulty/problem-name>');
console.log(' Example: yarn open easy/two-sum');
return;
}
// Resolve the problem path
const problemPath = resolveProblemPath(input);
if (!problemPath) {
console.log(`ā Problem not found: ${input}`);
console.log('š” Try: yarn open easy/two-sum');
process.exit(1);
}
// Extract and open the LeetCode link
const link = extractLeetCodeLink(problemPath);
if (!link) {
console.log(`ā No LeetCode link found in ${problemPath}`);
console.log('š” Make sure the file contains a line like: Link: https://leetcode.com/problems/...');
process.exit(1);
}
console.log(`šÆ Opening: ${path.basename(problemPath)}`);
console.log(`š Link: ${link}`);
openInBrowser(link);
}
main();