现象

Directus extensions 打印错误日志,只能打印出 javascript 堆栈,无法打印出 typescript 堆栈。

排查过程

  • 检查了输出目录 dist,发现 dist 目录下有对应的 map 文件,说明 rollup 打包确实生成了 map 文件。
  • 检查项目,确实安装了 source-map-support,而且在入口文件中引入了 source-map-support/register。
  • 进一步检查 map 文件, map 文件中确实包含了 typescript 的堆栈信息。
  • 分析打印出来的堆栈信息,发现堆栈信息中的路径后面带了一个时间戳参数,导致 source-map-support 无法正确解析到源文件。
[ERROR] directus-extension-FX - setup error Error: mock error
  at Object.setup (file:///Users/marsyas/Documents/Development/Projects/fx-engine/extensions/directus-extension-FX/dist/api.js?t=1729783263163:1:311160)
  at EventEmitter.<anonymous> (file:///Users/marsyas/Documents/Development/Projects/fx-engine/extensions/directus-extension-FX/dist/api.js?t=1729783263163:1:312108)
  at processTicksAndRejections (node:internal/process/task_queues:95:5)
  at async Promise.all (index 0)
  at Emitter.emitInit (file:///Users/marsyas/Documents/Development/Projects/fx-engine/node_modules/.pnpm/@directus+api@13.0.0/node_modules/@directus/api/dist/emitter.js:55:13)

解决方案

重写 source-map-support 的 retrieveFile 方法,去掉文件路径中的时间戳参数即可。

sourceMapSupport.install({
    retrieveFile: function (path) {
        path = path.trim();
        path = path.replace(/\?t=\d+/, "");
        if (/^file:/.test(path)) {
        path = path.replace(/file:\/\/\/(\w:)?/, function (protocol, drive) {
            return drive
            ? "" 
            : "/"; 
        });
        }
        if (path in fileContentsCache) {
        return fileContentsCache[path];
        }

        var contents = "";
        try {
        if (!fs) {
            var xhr = new XMLHttpRequest();
            xhr.open("GET", path, false);
            xhr.send(null);
            if (xhr.readyState === 4 && xhr.status === 200) {
            contents = xhr.responseText;
            }
        } else if (fs.existsSync(path)) {
            contents = fs.readFileSync(path, "utf8");
        }
        } catch (er) {
        }

        return (fileContentsCache[path] = contents);
    },
});

重新打印错误日志,即可看到正确的 typescript 堆栈信息:

[ERROR] directus-extension-FX - setup error Error: mock error
    at Object.setup (file:///Users/marsyas/Documents/Development/Projects/fx-engine/extensions/directus-extension-FX/src/mock/index.ts:32:11)
    at EventEmitter.<anonymous> (file:///Users/marsyas/Documents/Development/Projects/fx-engine/extensions/directus-extension-FX/src/hooks/init/index.ts:53:18)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Promise.all (index 0)
    at Emitter.emitInit (file:///Users/marsyas/Documents/Development/Projects/fx-engine/node_modules/.pnpm/@directus+api@13.0.0/node_modules/@directus/api/dist/emitter.js:55:13)

原因

继续追踪directus源码,发现在加载extensions时,为了支持热更新,使用了Cache Break技术,也就是会在路径后面加上时间戳参数,比如:

//api/src/extensions.ts
const endpointInstance: EndpointConfig | { default: EndpointConfig } = await import(
    `./${pathToRelativeUrl(endpointPath, __dirname)}?t=${Date.now()}`
);

这样导致source-map导致找不到对应文件,因而直接抛出了js错误堆栈。