TypeScript中如何从调用方文件路径解析相对路径?
基于调用方文件路径解析相对路径的问题
我有一个用于加载指定目录文件的函数,核心路径解析代码如下:
const dir = path.resolve('./bar') const dirents = await readdir(dir, { withFileTypes: true })
项目目录结构:
- loader.ts - foo - index.ts - bar - file.ts
执行脚本是foo/index.ts,从项目根目录执行命令:npx tsx foo/index.ts,其代码为:
import loader from '../loader' loader('./bar')
loader.ts的代码:
import path from "node:path"; export default async (dir: string) => { const resolved = path.resolve(dir) console.log('RESOLVED TO', resolved) }
目前的问题是,resolved会被解析为项目根目录下的/bar,而非调用方foo/index.ts所在目录下的foo/bar。import.meta.dirname只能获取当前loader.ts的目录,无法拿到调用方的上下文,项目是基础TypeScript项目,未使用webpack/babel等工具。
需求:基于调用方文件(index.ts)而非当前文件解析相对路径。
解决方案1:让调用方传递自身目录路径
这是最直接可靠的方式,让调用方主动传入自己的目录路径——调用方可以通过import.meta.url结合Node.js的工具方法拿到自身目录(需要在tsconfig.json中配置module为ESNext/NodeNext,moduleResolution为NodeNext)。
修改loader.ts:
import path from "node:path"; export default async (dir: string, callerDir: string) => { const resolved = path.resolve(callerDir, dir) console.log('RESOLVED TO', resolved) }
调用方foo/index.ts:
import loader from '../loader' import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; // 获取当前文件所在目录 const __dirname = dirname(fileURLToPath(import.meta.url)); loader('./bar', __dirname)
优缺点:
- 优点:实现简单,完全不依赖黑科技,兼容性和稳定性拉满
- 缺点:需要调用方额外传参,增加了调用成本
解决方案2:通过调用栈自动提取调用方路径
利用Node.js的Error栈信息,解析出调用方的文件路径,进而自动获取其目录,实现调用方无感知的路径解析。
修改loader.ts:
import path from "node:path"; import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; // 从调用栈中提取调用方的目录路径 function getCallerDir(): string { const originalPrepareStackTrace = Error.prepareStackTrace; try { const err = new Error(); Error.prepareStackTrace = (_, stack) => stack; const stack = err.stack as NodeJS.CallSite[]; // 栈层级说明:stack[0]是当前getCallerDir函数,stack[1]是loader函数,stack[2]才是调用loader的代码 const callerSite = stack[2]; if (!callerSite) throw new Error('无法获取调用方信息'); const callerFilePath = callerSite.getFileName(); if (!callerFilePath) throw new Error('无法获取调用方文件路径'); // 处理ESM模块的file://协议 if (callerFilePath.startsWith('file://')) { return dirname(fileURLToPath(callerFilePath)); } // 兼容CommonJS模块的情况 return dirname(callerFilePath); } finally { // 恢复原本的栈追踪函数 Error.prepareStackTrace = originalPrepareStackTrace; } } export default async (dir: string) => { const callerDir = getCallerDir(); const resolved = path.resolve(callerDir, dir) console.log('RESOLVED TO', resolved) }
调用方无需修改,直接执行loader('./bar')即可。
优缺点:
- 优点:调用方无需额外传参,使用完全透明
- 缺点:依赖Node.js特定的
Error.prepareStackTraceAPI,若代码被压缩混淆,栈信息可能失效;不同Node.js版本的栈格式可能略有差异,需要测试适配
额外配置说明
如果使用TypeScript,需确保tsconfig.json的模块配置正确:
{ "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext", "target": "ES2020", "esModuleInterop": true, "skipLibCheck": true } }
内容的提问来源于stack exchange,提问作者Cjmarkham
相关产品推荐
相关产品推荐

