UNPKG

tsconfig-replace-paths

Version:

Replace absolute paths to relative paths for package compilation

188 lines (187 loc) 7.49 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.replacePaths = replacePaths; const ts = require("typescript"); const fs_1 = require("fs"); const globby_1 = require("globby"); const path_1 = require("path"); const config_1 = require("./config"); const resolve_1 = require("./resolve"); function createVerboseLog(verbose, quiet) { return function (...args) { if (verbose && !quiet) { console.log(...args); } }; } function buildContext(options) { const cwd = options.cwd || process.cwd(); const project = options.project || 'tsconfig.json'; const configFile = (0, path_1.resolve)(cwd, project); const verboseLog = createVerboseLog(Boolean(options.verbose), Boolean(options.quiet)); verboseLog(`Using tsconfig: ${configFile}`); const returnedTsConfig = (0, config_1.loadConfig)(configFile); const { baseUrl, paths, outDir: tsConfigOutDir = '', rootDir: tsConfigRootDir = cwd, } = returnedTsConfig; if (!options.src && tsConfigRootDir === '') { console.error('Whoops! Please set compilerOptions.rootDir in your tsconfig or supply a flag'); throw new Error('--- exiting tsconfig-replace-paths due to parameters missing ---'); } if (!options.out && tsConfigOutDir === '') { console.error('Whoops! Please set compilerOptions.outDir in your tsconfig or supply a flag'); throw new Error('--- exiting tsconfig-replace-paths due to parameters missing ---'); } let usingSrcDir; if (options.src) { verboseLog('Using flag --src'); usingSrcDir = (0, path_1.resolve)(cwd, options.src); } else { verboseLog('Using compilerOptions.rootDir from your tsconfig'); usingSrcDir = (0, path_1.resolve)(cwd, tsConfigRootDir); } if (!usingSrcDir) { console.error(`Whoops! rootDir must be specified in your project => --project ${project}, or flagged with directory => --src './path'`); throw new Error('--- exiting tsconfig-replace-paths due to parameters missing ---'); } verboseLog(`Using src: ${usingSrcDir}`); let usingOutDir; if (options.out) { verboseLog('Using flag --out'); usingOutDir = (0, path_1.resolve)(cwd, options.out); } else { verboseLog('Using compilerOptions.outDir from your tsconfig'); usingOutDir = (0, path_1.resolve)(cwd, tsConfigOutDir); } if (!usingOutDir) { console.error(`Whoops! outDir must be specified in your project => --project ${project}, or flagged with directory => --out './path'`); throw new Error('--- exiting tsconfig-replace-paths due to parameters missing ---'); } verboseLog(`Using out: ${usingOutDir}`); if (!baseUrl) { throw new Error('compilerOptions.baseUrl is not set'); } if (!paths) { throw new Error('compilerOptions.paths is not set'); } verboseLog(`baseUrl: ${baseUrl}`); verboseLog(`rootDir: ${usingSrcDir}`); verboseLog(`outDir: ${usingOutDir}`); verboseLog(`paths: ${JSON.stringify(paths, null, 2)}`); const configDir = (0, path_1.dirname)(configFile); const basePath = (0, path_1.resolve)(configDir, baseUrl); const outPath = usingOutDir || (0, path_1.resolve)(basePath, usingOutDir); verboseLog(`basePath: ${basePath}`); verboseLog(`outPath: ${outPath}`); const aliases = (0, resolve_1.buildAliases)(paths, basePath); verboseLog(`aliases: ${JSON.stringify(aliases, null, 2)}`); return { configFile: configFile, basePath: basePath, usingSrcDir: usingSrcDir, outPath: outPath, aliases: aliases, verboseLog: verboseLog, }; } function getScriptKind(fileName) { if (fileName.endsWith('.tsx') || fileName.endsWith('.jsx')) { return ts.ScriptKind.TSX; } if (fileName.endsWith('.ts')) { return ts.ScriptKind.TS; } return ts.ScriptKind.JS; } function collectReplacements(sourceFile, outFile, absToRel) { const replacements = []; function visit(node) { if (ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) { const spec = node.moduleSpecifier; if (spec && ts.isStringLiteral(spec)) { const matched = spec.text; const replacement = absToRel(matched, outFile); if (replacement !== matched) { replacements.push({ start: spec.getStart(sourceFile) + 1, end: spec.getEnd() - 1, text: replacement, }); } } } if (ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.Identifier && node.expression.text === 'require' && node.arguments.length > 0 && ts.isStringLiteral(node.arguments[0])) { const arg = node.arguments[0]; const matched = arg.text; const replacement = absToRel(matched, outFile); if (replacement !== matched) { replacements.push({ start: arg.getStart(sourceFile) + 1, end: arg.getEnd() - 1, text: replacement, }); } } ts.forEachChild(node, visit); } visit(sourceFile); return replacements; } function applyReplacements(text, replacements) { replacements.sort(function (a, b) { return b.start - a.start; }); let result = text; for (let i = 0; i < replacements.length; i += 1) { const item = replacements[i]; result = result.substring(0, item.start) + item.text + result.substring(item.end); } return result; } function replaceAliasInText(text, outFile, absToRel) { const sourceFile = ts.createSourceFile(outFile, text, ts.ScriptTarget.Latest, true, getScriptKind(outFile)); const replacements = collectReplacements(sourceFile, outFile, absToRel); return applyReplacements(text, replacements); } function replacePaths(options) { const ctx = buildContext(options); const resolver = (0, resolve_1.createAliasResolver)(ctx); const quiet = Boolean(options.quiet); const check = Boolean(options.check); const files = (0, globby_1.sync)(`${ctx.outPath.replaceAll('\\', '/')}/**/*.{js,jsx,ts,tsx}`, { dot: true, noDir: true, }).map(function (x) { return (0, path_1.resolve)(x); }); const changedFiles = []; let changedFileCount = 0; for (let i = 0; i < files.length; i += 1) { const file = files[i]; const text = (0, fs_1.readFileSync)(file, 'utf8'); const prevCount = resolver.getReplaceCount(); const newText = replaceAliasInText(text, file, resolver.absToRel); if (text !== newText) { changedFileCount += 1; changedFiles.push(file); ctx.verboseLog(`${file}: replaced ${resolver.getReplaceCount() - prevCount} paths`); if (!check) { (0, fs_1.writeFileSync)(file, newText, 'utf8'); } } } const replaceCount = resolver.getReplaceCount(); if (!quiet) { console.log(`Replaced ${replaceCount} paths in ${changedFileCount} files`); } return { replaceCount: replaceCount, changedFileCount: changedFileCount, changedFiles: changedFiles, checkFailed: check && changedFileCount > 0, }; }