You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 23:58:11