[Scripts] Add a script to automatically rename files
This script automatically fixes any file name errors to ensure all files are named correctly. Use with caution, as I have not yet tested it much
This commit is contained in:
parent
318fc39b61
commit
3f058cbfd3
5
scripts/auto-renamer
Executable file
5
scripts/auto-renamer
Executable file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
SCRIPT_DIR=$(dirname "$0")
|
||||
|
||||
node "$SCRIPT_DIR/util/auto-renamer/index.js" $@
|
237
scripts/util/auto-renamer/index.js
Normal file
237
scripts/util/auto-renamer/index.js
Normal file
@ -0,0 +1,237 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { argv } = require('process');
|
||||
|
||||
const config = {
|
||||
// replace character of key with value, any character, apart from ['.', '_', '-', ' ']
|
||||
'replace': {
|
||||
'ä': 'ae',
|
||||
'ö': 'oe',
|
||||
'ü': 'ue',
|
||||
',': '-',
|
||||
'&': 'And',
|
||||
},
|
||||
'rules': {
|
||||
'underscore-before-and-after-number': true, // Will not do trailing or leading for filename
|
||||
'enforce-leading-zero': true, // adds a leading zero to any number below 10
|
||||
'camel-case-rules': {
|
||||
'enforce-snake-case-for-filetypes': ['py'], // using underscores
|
||||
'enforce-kebab-case-for-filetypes': ['css', 'html', 'scss', 'tex'], // using hyphens
|
||||
},
|
||||
'file-start-letter': 'lower', // lower, upper, unchanged
|
||||
'directory-start-letter': 'upper', // lower, upper, unchanged
|
||||
'replace-dots-with': '', // will not replace as leading character to not break dotfiles
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively find all files with extension in a directory
|
||||
* @param {string} dir The directory to search. Either absolute or relative path
|
||||
* @param {string} extension The file extension to look for
|
||||
* @param {string[]} ignoreList A list of filenames or directories to ignore
|
||||
* @returns {{ files: string, directories: string }} returns a list of html files with their full path
|
||||
*/
|
||||
const treeWalker = (dir, extension, ignoreList) => {
|
||||
const ls = fs.readdirSync(dir);
|
||||
const fileList = [];
|
||||
const dirList = [];
|
||||
for (let file in ls) {
|
||||
if (fs.statSync(path.join(dir, ls[file])).isDirectory()) {
|
||||
// Filter ignored directories
|
||||
if (ignoreList === undefined || !ignoreList.includes(ls[file])) {
|
||||
const newData = treeWalker(path.join(dir, ls[file]), extension, ignoreList);
|
||||
const newFiles = newData.files;
|
||||
dirList.push( path.join( dir, ls[ file ] ) );
|
||||
for (let dir = 0; dir < newData.directories.length; dir++) {
|
||||
dirList.push( newData.directories[dir] );
|
||||
}
|
||||
for (let file = 0; file < newFiles.length; file++) {
|
||||
fileList.push(newFiles[file]);
|
||||
}
|
||||
}
|
||||
} else if (extension == '*' || ls[file].includes(extension)) {
|
||||
if (ignoreList === undefined || !ignoreList.includes(ls[file])) {
|
||||
fileList.push(path.join(dir, ls[file]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { 'files': fileList, 'directories': dirList };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} filename The filename to fix according to the rules
|
||||
* @returns {string} the fixed filename
|
||||
*/
|
||||
const fixName = ( fn, ft ) => {
|
||||
let out = '';
|
||||
const enforceSnake = config.rules['camel-case-rules']['enforce-snake-case-for-filetypes'].includes( ft );
|
||||
const enforceKebab = config.rules['camel-case-rules']['enforce-kebab-case-for-filetypes'].includes( ft );
|
||||
const isDir = ft === 'directory';
|
||||
const startLetter = isDir ? config.rules['directory-start-letter'] : config.rules['file-start-letter'];
|
||||
let nextUpperCase = false;
|
||||
|
||||
for ( let i = 0; i < fn.length; i++ ) {
|
||||
const c = fn[i];
|
||||
|
||||
if ( c == '.' ) {
|
||||
// Rule: Removed after number, allowed elsewhere
|
||||
if ( i > 0 && /[0-9]/.test( fn[ i - 1 ] ) ) {
|
||||
out += config.rules[ 'replace-dots-with' ];
|
||||
} else {
|
||||
out += '.';
|
||||
}
|
||||
} else if ( /[A-Z]/.test( c ) ) {
|
||||
// If we reach a capital letter and enforce either kebab-case or snake_case, we can assume that this is the start of a CamelCase word
|
||||
if ( enforceKebab ) {
|
||||
out += '-' + c.toLowerCase();
|
||||
} else if ( enforceSnake ) {
|
||||
out += '_' + c.toLowerCase();
|
||||
} else {
|
||||
nextUpperCase = false;
|
||||
if ( i == 0 && startLetter === 'lower' ) {
|
||||
out += c.toLowerCase();
|
||||
} else {
|
||||
out += c;
|
||||
}
|
||||
}
|
||||
} else if ( c == ' ' ) {
|
||||
// We always replace spaces, the question is just to what
|
||||
if ( enforceKebab ) {
|
||||
out += '-';
|
||||
} else if ( enforceSnake ) {
|
||||
out += '_';
|
||||
} else {
|
||||
nextUpperCase = true;
|
||||
}
|
||||
} else if ( c == '_' ) {
|
||||
// If we are not enforcing snake_case, then replace it
|
||||
if ( !enforceSnake ) {
|
||||
if ( needsUnderscore( i, fn ) ) {
|
||||
out += '_';
|
||||
} else if ( enforceKebab ) {
|
||||
out += '-';
|
||||
} else {
|
||||
nextUpperCase = true;
|
||||
}
|
||||
} else {
|
||||
out += '_'
|
||||
}
|
||||
} else if ( c == '-' ) {
|
||||
// If we are not enforcing kebab-case
|
||||
if ( !enforceKebab ) {
|
||||
if ( enforceSnake ) {
|
||||
out += '_';
|
||||
} else {
|
||||
nextUpperCase = true;
|
||||
}
|
||||
} else {
|
||||
out += '-'
|
||||
}
|
||||
} else {
|
||||
let curr = config.replace[ c ] === undefined ? c : config.replace[ c ];
|
||||
if ( config.rules[ 'underscore-before-and-after-number' ] || config.rules['enforce-leading-zero'] ) {
|
||||
if ( /[0-9]/.test( c ) ) {
|
||||
if ( i < fn.length - 1 ) {
|
||||
if ( config.rules['enforce-leading-zero'] ) {
|
||||
const prevIsNumber = i > 0 && /[0-9]/.test( fn[i - 1] );
|
||||
const nextIsNumber = /[0-9]/.test( fn[i + 1] );
|
||||
if ( !nextIsNumber && ( i == 0|| !prevIsNumber ) ) {
|
||||
curr = '0' + curr;
|
||||
}
|
||||
}
|
||||
|
||||
if ( config.rules['underscore-before-and-after-number'] ) {
|
||||
if ( !( /[0-9]/.test( fn[ i + 1 ] ) ) && fn[ i + 1 ] != '_' ) {
|
||||
curr += '_';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ( config.rules['enforce-leading-zero'] && ( i > 0 && !( /[0-9]/.test( fn[i - 1] ) ) ) ) {
|
||||
curr = '0' + curr;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ( config.rules['underscore-before-and-after-number'] && /[0-9]/.test( fn[ i + 1 ] ) ) {
|
||||
curr += '_';
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( nextUpperCase || ( i == 0 && startLetter === 'upper' ) ) {
|
||||
nextUpperCase = false;
|
||||
out += curr.toUpperCase();
|
||||
} else {
|
||||
if ( i == 0 && startLetter === 'upper' ) {
|
||||
out += curr.toUpperCase();
|
||||
} else {
|
||||
out += curr.toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const needsUnderscore = ( i, fn ) => {
|
||||
return ( i > 0 && /[0-9]/.test( fn[ i - 1 ] ) ) || ( i < fn - 1 && /[0-9]/.test( fn[ i + 1 ] ) )
|
||||
}
|
||||
|
||||
const separateDirAndFileAndFiletype = ( filename ) => {
|
||||
const loc = filename.lastIndexOf( '/' ) + 1;
|
||||
let ftl = filename.lastIndexOf( '.' ) + 1;
|
||||
let fn = filename.substring( loc, ftl - 1 );
|
||||
let ft = filename.substring( ftl );
|
||||
if ( fs.statSync( filename ).isDirectory() ) {
|
||||
ftl = filename.length;
|
||||
fn = filename.substring( loc );
|
||||
ft = 'directory';
|
||||
|
||||
}
|
||||
const dir = filename.slice( 0, loc - 1 );
|
||||
return { 'filename': fn, 'dir': dir, 'filetype': ft };
|
||||
}
|
||||
|
||||
const fixDirName = ( directory, top ) => {
|
||||
if ( directory === top ) {
|
||||
return top;
|
||||
}
|
||||
|
||||
const f = separateDirAndFileAndFiletype( directory );
|
||||
return fixDirName( f.dir, top ) + '/' + fixName( f.filename, f.filetype );
|
||||
}
|
||||
|
||||
if (argv[2] == '-h') {
|
||||
console.log('auto-renamer [directory]\n\n=> Recursively rename files in directory');
|
||||
} else if (argv[2] == '-v') {
|
||||
console.log('auto-renamer version 1.0.0, developed by Janis Hutz (development@janishutz.com)');
|
||||
} else {
|
||||
// Recursively add all files in the directory
|
||||
const fp = path.resolve( argv[2] );
|
||||
const list = treeWalker(fp, '*', ['.git', '@girs']);
|
||||
const files = list.files;
|
||||
const directories = list.directories;
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
const f = separateDirAndFileAndFiletype( file );
|
||||
let fixedFile = fixName( f.filename, f.filetype );
|
||||
|
||||
// Rename
|
||||
const fixedPath = f.dir + '/' + fixedFile + '.' + f.filetype;
|
||||
console.log( file + ' -> ' + fixedPath );
|
||||
fs.renameSync( file, fixedPath );
|
||||
}
|
||||
|
||||
// Fix directory names after file names. Sort array by decending length
|
||||
directories.sort( ( a, b ) => {
|
||||
return b.length - a.length;
|
||||
} );
|
||||
|
||||
// separate directories up until we reach the path of dir started in
|
||||
for (let i = 0; i < directories.length; i++) {
|
||||
const dir = directories[i];
|
||||
const fixed = fixDirName( dir, fp );
|
||||
console.log( dir + ' -> ' + fixed );
|
||||
fs.renameSync( dir, fixDirName( dir, fp ) );
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user