Dev
Web
Scripts
Rename Files

Rename Files

Rename All Files and Folders in src to kebab-case

// rename-files.js
import fs from 'fs'
import { execSync } from 'node:child_process'
import path from 'node:path'
 
function toKebabCase(str) {
  return str
    .replace(/([a-z\d])([A-Z])/g, '$1-$2') // camelCase or PascalCase to kebab-case
    .replace(/([A-Z]+)([A-Z][a-z\d]+)/g, '$1-$2') // Handle cases like XMLHTTPRequest
    .replace(/[\s_]+/g, '-') // spaces or underscores to dashes
    .toLowerCase()
}
 
function isTrackedByGit(filePath) {
  try {
    execSync(`git ls-files --error-unmatch "${filePath}"`, { stdio: 'ignore' })
    return true
  } catch {
    return false
  }
}
 
function renameWithGitMv(oldPath, newPath) {
  if (!fs.existsSync(oldPath)) {
    console.info(`Skipped (source doesn't exist): ${oldPath}`)
    return
  }
 
  if (oldPath === newPath) {
    console.info(`Skipped (already renamed): ${oldPath}`)
    return
  }
 
  if (isTrackedByGit(oldPath)) {
    const tempPath = `${newPath}_temp_${Date.now()}`
    execSync(`git mv "${oldPath}" "${tempPath}"`)
    execSync(`git mv "${tempPath}" "${newPath}"`)
    console.info(`Renamed (git mv): ${oldPath} -> ${newPath}`)
  } else {
    fs.renameSync(oldPath, newPath)
    console.info(`Renamed (fs.renameSync): ${oldPath} -> ${newPath}`)
  }
}
 
function convertSingleIndexFolders(dir) {
  const entries = fs.readdirSync(dir)
 
  entries.forEach((entry) => {
    const oldPath = path.join(dir, entry)
    const stat = fs.statSync(oldPath)
 
    if (stat.isDirectory()) {
      const subEntries = fs.readdirSync(oldPath)
      if (subEntries.length === 1 && subEntries[0] === 'index.tsx') {
        const newFileName = `${toKebabCase(entry)}.tsx`
        const newPath = path.join(dir, newFileName)
        const indexFilePath = path.join(oldPath, 'index.tsx')
 
        renameWithGitMv(indexFilePath, newPath)
        fs.rmdirSync(oldPath)
        console.info(`Converted folder to file: ${oldPath} -> ${newPath}`)
        return
      }
    }
 
    if (stat.isDirectory()) {
      convertSingleIndexFolders(oldPath) // Recursively handle subdirectories
    }
  })
}
 
function renameFilesAndFolders(dir) {
  const entries = fs.readdirSync(dir).sort((a, b) => b.length - a.length)
 
  entries.forEach((entry) => {
    const oldPath = path.join(dir, entry)
    const stat = fs.statSync(oldPath)
 
    const newEntry = toKebabCase(entry)
    const newPath = path.join(dir, newEntry)
 
    if (oldPath !== newPath) {
      // Skip if already in kebab case
      if (entry === newEntry) {
        console.info(`Skipped (already kebab case): ${oldPath}`)
        return
      }
 
      // Rename files or directories using git mv
      renameWithGitMv(oldPath, newPath)
 
      if (stat.isDirectory()) {
        // Recursively rename contents if it's a directory
        renameFilesAndFolders(newPath)
      }
      console.info(`Renamed: ${oldPath} -> ${newPath}`)
    } else if (stat.isDirectory()) {
      // Process contents of directories that are already in kebab-case
      renameFilesAndFolders(newPath)
    }
  })
}
 
convertSingleIndexFolders('./src') // Convert single index.tsx folders first
renameFilesAndFolders('./src') // Replace with your root directory if different

Then run:

node rename-files.js