Javascript 重构别名@imports到相对路径

Javascript 重构别名@imports到相对路径,javascript,visual-studio-code,refactoring,jetbrains-ide,es6-modules,Javascript,Visual Studio Code,Refactoring,Jetbrains Ide,Es6 Modules,在使用Webpack、TypeScript或其他转换ES模块导入的工具的模块化环境中,使用路径别名,这是一种常见的约定 转换具有别名绝对路径的项目是我经常遇到的问题: src/foo/bar/index.js 到相对路径: src/foo/bar/index.js 例如,使用别名的项目需要与另一个不使用别名的项目合并,由于样式指南或其他原因,将后者配置为使用别名不是一个选项 这无法通过简单的搜索和替换来解决,手动修复导入路径非常繁琐且容易出错。我希望原始JavaScript/TypeScript

在使用Webpack、TypeScript或其他转换ES模块导入的工具的模块化环境中,使用路径别名,这是一种常见的约定

转换具有别名绝对路径的项目是我经常遇到的问题:

src/foo/bar/index.js

到相对路径:

src/foo/bar/index.js

例如,使用别名的项目需要与另一个不使用别名的项目合并,由于样式指南或其他原因,将后者配置为使用别名不是一个选项

这无法通过简单的搜索和替换来解决,手动修复导入路径非常繁琐且容易出错。我希望原始JavaScript/TypeScript代码库在其他方面保持不变,因此使用transpiler转换它可能不是一个选项

我希望使用我选择的IDE(Jetbrains IDEA/Webstorm/Phpstorm)实现这种重构,但也会接受使用任何其他IDE(VS代码)或plain Node.js的解决方案


如何做到这一点?

来自您链接的帖子:

模块标识符的含义和结构取决于模块加载器或模块绑定器

这意味着,对于这个'@'->相对导入转换,永远不会有一刀切的解决方案。您可以做的是使用一些程序来指定
@
对于给定项目意味着什么,这应该是一个相当不错的解决方案

我想我应该做的是,创建一个检查在导入语句中包含
@
的文件,并确定它必须遍历多少父目录才能到达用户确定的根目录,然后将
@
符号替换为适当数量的
。/

一旦你制作了一个codemod/program/script来实现这一点,你可以从你选择的编辑器中触发它


这至少是我解决这个问题的方法(除了在开始之前寻找我可以应用的预制解决方案!)

三种可能的解决方案,它们可以将别名导入重新布线到相对路径:

1.巴贝尔插件模块解析器 使用,同时省去其他babel插件/预设

.babelrc
"plugins": [
  [
    "module-resolver",
    {
      "alias": {
        "^@/(.+)": "./src/\\1"
      }
    }
  ]
]
构建步骤:
babel src--out dir dist
(在
dist
中输出,不会就地修改)

已处理的示例文件:
// input                                // output
import { helloWorld } from "@/sub/b"    // import { helloWorld } from "./sub/b";
import "@/sub/b"                        // import "./sub/b";
export { helloWorld } from "@/sub/b"    // export { helloWorld } from "./sub/b";
export * from "@/sub/b"                 // export * from "./sub/b";
对于TS,您还需要通过
babel src--out dir dist--extensions.TS
激活
.TS
扩展

2.带有正则表达式的Codemod jscodeshift 应支持中的所有相关导入/导出变体。算法的实现方式如下:

...
├── package.json
└── src
    └── components
一,。输入:路径别名映射形式为
alias->resolved path
,类似于TypeScript
tsconfig.json
path
或网页包的
resolve.alias

const pathMapping = {
  "@": "./custom/app/path",
  ...
};
二,。迭代所有源文件,例如遍历
src

jscodeshift-t scripts/jscodeshift.js src#使用-d-p选项进行干运行+stdout
#还是给TS
jscodeshift--extensions=ts--parser=ts-t scripts/jscodeshift.js src
三,。对于每个源文件,查找所有导入和导出声明

function transform(file, api) {
  const j = api.jscodeshift;
  const root = j(file.source);

  root.find(j.ImportDeclaration).forEach(replaceNodepathAliases);
  root.find(j.ExportAllDeclaration).forEach(replaceNodepathAliases);
  root
    .find(j.ExportNamedDeclaration, node => node.source !== null)
    .forEach(replaceNodepathAliases);
  return root.toSource();
 ...
};
jscodeshift.js
/**
*对应于tsconfig.json路径或网页包别名
*例如“@/app/store/AppStore”->”/src/app/store/AppStore”
*/
常量路径映射={
“@”:“/src”,
福:“酒吧”,
};
const replacePathAlias=require(“./replace path alias”);
module.exports=函数转换(文件、api){
const j=api.jscodeshift;
const root=j(file.source);
根发现(j.ImportDeclaration.forEach(replacemendodepataliases);
根.find(j.ExportAllDeclaration).forEach(replaceNodePataliases);
/**
*过滤掉正常的模块导出,比如导出函数foo(){…}
*包括从“mymodule”等导出{a}。
*/
根
.find(j.exportnamedclaration,(node)=>node.source!==null)
.forEach(替换性足类);
返回root.toSource();
函数replaceNodePataliases(impExpDeclNodePath){
impExpDeclNodePath.value.source.value=replacePathAlias(
file.path,
impExpDeclNodePath.value.source.value,
路径映射
);
}

};我创建了一个脚本来执行此操作

它基本上遍历项目树,搜索所有文件,查找类似“@/my/import”的导入,并使用regex
/“@(\/\w+[\w\/.]+)”/gi
,然后使用nodejs模块创建相对路径

我希望你没有任何我在这个简单的脚本中没有涉及的边缘案例,所以最好备份你的文件。我只在一个简单的场景中测试了它

以下是:

请确保提供目录名作为参数。例如,像
src

对于IDE部分,我知道Jetbrains Webstorm允许您定义npm任务
创建一个
scripts
目录来保存脚本
package.json中定义脚本

"scripts": {
    ...
    "replaceimports": "node scripts/script.js \"src\""
}

在npm工具窗口中注册npm任务以供使用。

一个大大减少任务时间的简单方法是只对位于特定深度级别的目标文件使用regexp模式匹配。假设您有一个指向
组件
文件夹的神奇路径和如下项目结构:

...
├── package.json
└── src
    └── components
您可以通过简单的查找和替换来重构它:

find: from "components
replace: from "../components
files to include: ./src/*/**.ts
然后你就递归地去:

find: from "components
replace: from "../../components
files to include: ./src/*/*/**.ts

我为此写了一篇小博文:

谢谢。我的意图是去掉
@
,使
@/
不会出现在代码库的任何地方。将一个项目与另一个项目合并意味着将以前是单独项目的源文件复制到不使用别名的项目中。我不完全理解babel选项。这似乎是100%的好,它会覆盖您当前的文件在磁盘上吗?如果是这样,我们应该添加一个注释,以确保
git状态
是干净的(或者只提到源代码将在磁盘上修改)。我可能误解了这个答案…所以,这是actu
find: from "components
replace: from "../components
files to include: ./src/*/**.ts
find: from "components
replace: from "../../components
files to include: ./src/*/*/**.ts