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

如何在TypeScript转换器中检测导入路径是否为目录?

检测TypeScript导入/导出路径是否为目录的实现方法

要实现给目录导入追加/index.js、给文件导入追加.js的需求,核心是通过TypeScript的模块解析能力结合文件系统校验,判断导入路径对应的是目录还是文件。

实现步骤

  1. 利用TypeScript Program的模块解析能力:通过resolveModuleName方法准确获取TS实际解析的模块路径,适配tsconfig中的baseUrl、paths等配置。
  2. 文件系统校验:通过fs模块判断导入路径对应的物理资源是否为目录。
  3. 修改Transformer逻辑:根据检测结果生成对应的模块标识符。

完整修改后的代码

import typescript from "typescript";
import * as fs from "fs";
import * as path from "path";

export default function transformer(program: typescript.Program) {
  return (transformationContext: typescript.TransformationContext) =>
    (sourceFile: typescript.SourceFile) => {
      function shouldMutateModuleSpecifier(node: typescript.Node): node is typescript.ImportDeclaration | typescript.ExportDeclaration {
        if (!typescript.isImportDeclaration(node) && !typescript.isExportDeclaration(node)) {
          return false;
        }
        if (!node.moduleSpecifier || !typescript.isStringLiteral(node.moduleSpecifier)) {
          return false;
        }
        const specifier = node.moduleSpecifier.text;
        // 仅处理相对/绝对路径的无扩展名导入,排除第三方模块
        return (specifier.startsWith('./') || specifier.startsWith('../') || specifier.startsWith('/')) 
          && !path.extname(specifier);
      }

      function isDirectoryImport(moduleSpecifier: string, sourceFile: typescript.SourceFile): boolean {
        const baseDir = path.dirname(sourceFile.fileName);
        const resolvedPath = path.resolve(baseDir, moduleSpecifier);

        // 直接校验物理路径是否为目录
        if (fs.existsSync(resolvedPath)) {
          return fs.statSync(resolvedPath).isDirectory();
        }

        // 利用TS内置模块解析,判断是否指向目录下的index文件
        const resolveResult = typescript.resolveModuleName(
          moduleSpecifier,
          sourceFile.fileName,
          program.getCompilerOptions(),
          program.getModuleResolutionHost()
        );

        if (resolveResult.resolvedModule) {
          const resolvedFileName = resolveResult.resolvedModule.resolvedFileName;
          const resolvedDir = path.dirname(resolvedFileName);
          // 若解析结果是index文件,且原路径对应该目录,则判定为目录导入
          return path.basename(resolvedFileName).startsWith('index') 
            && resolvedDir === path.resolve(baseDir, moduleSpecifier);
        }

        return false;
      }

      function visitNode(node: typescript.Node): typescript.VisitResult<typescript.Node> {
        if (shouldMutateModuleSpecifier(node)) {
          let newSpecifierText = node.moduleSpecifier.text;
          
          if (isDirectoryImport(newSpecifierText, sourceFile)) {
            newSpecifierText = `${newSpecifierText}/index.js`;
          } else {
            newSpecifierText = `${newSpecifierText}.js`;
          }

          const newModuleSpecifier = typescript.factory.createStringLiteral(newSpecifierText);

          if (typescript.isImportDeclaration(node)) {
            return typescript.factory.updateImportDeclaration(
              node,
              node.modifiers,
              node.importClause,
              newModuleSpecifier,
              undefined
            );
          }

          if (typescript.isExportDeclaration(node)) {
            return typescript.factory.updateExportDeclaration(
              node,
              node.modifiers,
              false,
              node.exportClause,
              newModuleSpecifier,
              undefined
            );
          }
        }

        return typescript.visitEachChild(node, visitNode, transformationContext);
      }

      return typescript.visitNode(sourceFile, visitNode);
    };
}

关键逻辑说明

  • shouldMutateModuleSpecifier:过滤出需要处理的导入/导出声明,仅针对无扩展名的相对/绝对路径导入,避免修改第三方模块或已有扩展名的文件。
  • isDirectoryImport:
    • 先通过fs.existsSync和fs.statSync直接校验物理路径是否为目录;
    • 若物理路径不存在(比如TS通过paths映射的目录),则用resolveModuleName获取TS的解析结果,判断是否指向目录下的index文件。
  • 模块标识符生成:根据检测结果,目录导入追加/index.js,文件导入追加.js。

内容的提问来源于stack exchange,提问作者Carlo Corradini

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 10:14:53