Skip to content

Instantly share code, notes, and snippets.

@Moizsohail
Created July 25, 2023 07:21
Show Gist options
  • Save Moizsohail/17b08f5800c5959f9528754a254080ef to your computer and use it in GitHub Desktop.
Save Moizsohail/17b08f5800c5959f9528754a254080ef to your computer and use it in GitHub Desktop.
This is a utilitly program allowing you to swap relative paths to the shortest tailwind alias by reading the tsconfig.json
/* eslint-disable no-console */
/*
Alias Absolute Resolver
This is a utilitly program allowing you to swap relative paths to the shortest tailwind alias.
How to run?
npx ts-node aliasAbsolver.ts
*/
const fs = require('fs');
const { exit } = require('process');
// import config from './tsconfig.json';
const tsconfig = require('./tsconfig.json');
const packagejson = require('./package.json');
function getArgs() {
const args = {};
process.argv.slice(2, process.argv.length).forEach((arg) => {
if (arg.slice(0, 2) === '--') {
const longArg = arg.split('=');
const longArgFlag = longArg[0].slice(2, longArg[0].length);
const longArgValue = longArg.length > 1 ? longArg[1] : true;
args[longArgFlag] = longArgValue;
}
});
return args;
}
const getAllTsFilesFromFolder = (dir) => {
let results: string[] = [];
fs.readdirSync(dir).forEach((file) => {
const filePath = `${dir}/${file}`;
const stat = fs.statSync(filePath);
if (stat && stat.isDirectory()) {
results = results.concat(getAllTsFilesFromFolder(filePath));
} else results.push(filePath);
});
return results.filter((filePath) => filePath.endsWith('.ts'));
};
interface ExpectedArgs {
pattern?: string;
}
interface ValidatedArgs {
pattern: string | null;
}
const argHelper = `
Invalid Arguments
Allowed Args are:\n
--pattern={string} : Matches a list of file to resolve. The program performs a search on the text. This should not be a magic or regex expression.
`;
const validateArgs = (args: ExpectedArgs): ValidatedArgs => {
const pattern = args.pattern ?? null;
if (typeof pattern !== 'string') throw Error(argHelper);
return { pattern };
};
const args: ExpectedArgs = getArgs();
const { pattern } = validateArgs(args);
const getAliases = () => {
const aliases = tsconfig?.compilerOptions?.paths ?? {};
const updatedAliases: { [alias: string]: string[] } = {};
Object.entries(aliases).forEach((entry) => {
const [alias, aliasPath]: any = entry;
const cleanAlias = alias.replace(/\*$/, '');
updatedAliases[cleanAlias] = aliasPath[0].replace(/^.\//, '').replace(/\*$/, '');
});
return updatedAliases;
};
const availableAliases = getAliases();
if (Object.keys(availableAliases).length === 0) {
exit();
}
const tsAliases = new Set(Object.keys(availableAliases).map((path) => path.replace('*', '')));
let allFiles = getAllTsFilesFromFolder('./src');
if (pattern) allFiles = allFiles.filter((filePath) => filePath.includes(pattern));
const importRegex = /^import\s+\{[\s\w,]+\}\s+from\s+['"][@\w/.-]+['"];$/gm;
const importPathRegex = /['"](.+)['"]/;
interface ImportDetails {
path: string;
fullImport: string;
index: number;
}
const parseImport = (data: string) => {
// const matches = importRegex.exec(data);
let match;
const results: ImportDetails[] = [];
// eslint-disable-next-line no-cond-assign
while ((match = importRegex.exec(data))) {
const importPath = match[0].match(importPathRegex);
results.push({
path: importPath[1],
fullImport: match[0],
index: match.index,
});
}
return results;
};
const resolveRelativeFromAbsolute = (relativePath: string, absolutePath, split = '/') => {
const replace = /[/|\\]/g;
const relativePieces = relativePath.replace(replace, split).split(split);
const absolutePieces = absolutePath.replace(replace, split).split(split);
if (absolutePieces[1] === relativePieces[0]) {
return relativePath;
}
const numberOfBacks = relativePieces.filter((file) => file === '..').length;
return [...absolutePieces.slice(0, -(numberOfBacks + 1)), ...relativePieces.filter((file) => file !== '..' && file !== '.')].join(split).replace(/.\//, '');
};
const deps = new Set([...Object.keys(packagejson.dependencies), ...Object.keys(packagejson.devDependencies)].map((depName) => depName.split('/')[0]));
const absolver = (importPath, filePath) => {
const importPrefix = importPath.split('/')[0];
if (deps.has(importPrefix) || tsAliases.has(importPrefix)) return null;
const absPath = resolveRelativeFromAbsolute(importPath, filePath);
let found: string = '';
let respectiveAliasPath: string = '';
Object.entries(availableAliases).forEach((entry) => {
const [alias, aliasPath]: any[] = entry;
// console.log(aliasPath);
if (absPath.startsWith(aliasPath)) {
if (respectiveAliasPath.length < aliasPath.length) {
found = alias;
respectiveAliasPath = aliasPath;
}
}
});
if (!found) return null;
const newPath = absPath.replace(respectiveAliasPath, found);
return { importPath, newPath };
};
function escapeRegExp(string) {
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
const processFileContent = (data, filePath) => {
const importData = parseImport(data);
let newData = data;
const replacements = importData.map(({ path: importPath }) => absolver(importPath, filePath)).filter((replacement) => replacement !== null);
if (replacements.length === 0) return null;
replacements.forEach(({ newPath, importPath }:any) => {
const excapedImportPath = escapeRegExp(importPath);
const regexString = `^(import\\s+\\{[\\s\\w,]+\\}\\s+from\\s+['"])${excapedImportPath}(['"];?)$`;
const regex = new RegExp(regexString, 'm');
newData = newData.replace(regex, `$1${newPath}$2`);
});
return newData;
// data.replace(new RegExp())
};
const processFile = (filePath) => {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
console.error(`File Parsing Failed for ${filePath}`);
console.error(`Error:${err}`);
return;
}
const newData = processFileContent(data, filePath);
if (!newData) { console.error('No update', filePath); }
fs.writeFile(filePath, newData, (error) => {
if (err) { console.error('Update failed', error); } else {
console.log('Updated: ', filePath);
}
});
});
};
processFile(allFiles[1]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment