Skip to content

Instantly share code, notes, and snippets.

@rifler
Last active August 30, 2018 19:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rifler/e6123e90e172814fe23d5a6a11ed5eb1 to your computer and use it in GitHub Desktop.
Save rifler/e6123e90e172814fe23d5a6a11ed5eb1 to your computer and use it in GitHub Desktop.
AbsoluteToRelative
const fs = require('fs');
const nodePath = require('path');
const paths = require('tsconfig-paths');
// этот модуль умеет рекурсивно раскрывать extends
const tsconfig = require('tsconfig-extends');
const Project = require('ts-simple-ast').default;
module.exports = async ({
tsconfigPath: incomingTSConfigPath,
filesGlob,
cwd = process.cwd(),
}) => {
let tsconfigPath = incomingTSConfigPath;
if (!nodePath.isAbsolute(tsconfigPath)) {
tsconfigPath = nodePath.normalize(nodePath.join(cwd, tsconfigPath));
}
const compilerOptions = tsconfig.load_file_sync(tsconfigPath);
const absoluteBaseUrl = nodePath.join(cwd, compilerOptions.baseUrl);
const matchPathFunc = paths.createMatchPath(
absoluteBaseUrl,
compilerOptions.paths || {},
);
const project = new Project({ compilerOptions });
project.addExistingSourceFiles(filesGlob);
const sourceFiles = project.getSourceFiles();
let anyFileWasChanged = false;
for (const sourceFile of sourceFiles) {
// находим все импорты и экспорты
const importExportDeclarations = [
...sourceFile.getImportDeclarations(),
...sourceFile.getExportDeclarations(),
];
const sourceFileAbsolutePath = sourceFile.getFilePath();
const sourceFileAbsoluteDirName = nodePath.dirname(
sourceFileAbsolutePath,
);
for (const declaration of importExportDeclarations) {
// у экспорта может не быть "module specifier", например:
// export { SomeClass };
const isEmptyExportDeclaration =
declaration.hasModuleSpecifier &&
!declaration.hasModuleSpecifier();
// если модуль выглядит как абсолютный, например import ... from 'icons';
if (
!isEmptyExportDeclaration &&
!declaration.isModuleSpecifierRelative()
) {
// получаем имя модуля
// (например: 'components', 'components/SomeModule', 'components/SomeModule/index')
const value = declaration.getModuleSpecifierValue();
// Пробуем найти относительный модуль по имени
// Если имя - это существующий путь до папки - функция вернет относительный путь
// Если такой папки нет, то пробуем подставлять каждое расширение в конец строки
// и проверять существование файла (напр. components/SomeModule/index(.tsx|.ts|.js))
// Если найден хоть один файл - возвращаем относительный путь до него
// Если файлы не найдены - undefined
const absolutePathToDepsModule = matchPathFunc(
value,
undefined,
fs.existsSync,
['.tsx', '.ts', '.jsx', '.js'],
);
// и если такое совпадение найдено
if (absolutePathToDepsModule) {
const resultPath = nodePath.relative(
sourceFileAbsoluteDirName,
absolutePathToDepsModule,
);
// применяем его
// import { IconToggler } from 'icons';
// ↓
// import { IconToggler } from '../../..';
declaration.setModuleSpecifier(resultPath);
anyFileWasChanged = true;
}
}
}
}
if (anyFileWasChanged) {
await project.save();
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment