将别名@Imports重构为相对路径

Refactor aliased @ imports to relative paths(将别名@Imports重构为相对路径)
本文介绍了将别名@Imports重构为相对路径的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

src/foo/bar/index.js

import baz from '@/baz';

到相对路径:

src/foo/bar/index.js

import baz from '../../baz';

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

这不能通过简单的搜索和替换来解决,手动修复导入路径既繁琐又容易出错。我希望原始JavaScript/TypeScript代码库在其他方面保持不变,因此可能不会使用代码转换程序对其进行转换。

我希望使用我选择的IDE(JetBrains Idea/WebStorm/PhpStorm)实现这种重构,但接受使用任何其他IDE(VS代码)或普通Node.js的解决方案。

如何实现此目标?

推荐答案

将别名导入重新连接到相对路径的三种可能的解决方案:

1.babel-plugin-module-solver

使用babel-plugin-module-resolver,同时省略其他巴别塔插件/预设。

.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/preset-typescript并通过babel src --out-dir dist --extensions ".ts"激活.ts扩展。

2.使用Regex进行Codemod jcodesShift

应支持MDNdocs中的所有相关导入/导出变体。该算法的实现方式如下:

1.输入:路径别名映射,格式为alias -> resolved path类似于打字稿tsconfig.jsonpaths或webpack的resolve.alias

const pathMapping = {
  "@": "./custom/app/path",
  ...
};

2.遍历源文件,例如遍历src

jscodeshift -t scripts/jscodeshift.js src # use -d -p options for dry-run + stdout
# or for TS
jscodeshift --extensions=ts --parser=ts -t scripts/jscodeshift.js src

3.对于每个源文件,查找所有导入和导出声明

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

/**
 * Corresponds to tsconfig.json paths or webpack aliases
 * E.g. "@/app/store/AppStore" -> "./src/app/store/AppStore"
 */
const pathMapping = {
  "@": "./src",
  foo: "bar",
};

const replacePathAlias = require("./replace-path-alias");

module.exports = 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);

  /**
   * Filter out normal module exports, like export function foo(){ ...}
   * Include export {a} from "mymodule" etc.
   */
  root
.find(j.ExportNamedDeclaration, (node) => node.source !== null)
.forEach(replaceNodepathAliases);

  return root.toSource();

  function replaceNodepathAliases(impExpDeclNodePath) {
impExpDeclNodePath.value.source.value = replacePathAlias(
  file.path,
  impExpDeclNodePath.value.source.value,
  pathMapping
);
  }
};

进一步说明:

import { AppStore } from "@/app/store/appStore-types"

创建以下AST,ImportDeclaration个节点的source.value可以修改:

4.对于每个路径声明,测试包含其中一个路径别名的Regex模式。

5.获取别名的解析路径,转换为相对于当前文件位置的路径(积分为@reijo)

replace-path-alias.js(4.+5.):

const path = require("path");

function replacePathAlias(currentFilePath, importPath, pathMap) {
  // if windows env, convert backslashes to "/" first
  currentFilePath = path.posix.join(...currentFilePath.split(path.sep));

  const regex = createRegex(pathMap);
  return importPath.replace(regex, replacer);

  function replacer(_, alias, rest) {
const mappedImportPath = pathMap[alias] + rest;

// use path.posix to also create foward slashes on windows environment
let mappedImportPathRelative = path.posix.relative(
  path.dirname(currentFilePath),
  mappedImportPath
);
// append "./" to make it a relative import path
if (!mappedImportPathRelative.startsWith("../")) {
  mappedImportPathRelative = `./${mappedImportPathRelative}`;
}

logReplace(currentFilePath, mappedImportPathRelative);

return mappedImportPathRelative;
  }
}

function createRegex(pathMap) {
  const mapKeysStr = Object.keys(pathMap).reduce((acc, cur) => `${acc}|${cur}`);
  const regexStr = `^(${mapKeysStr})(.*)$`;
  return new RegExp(regexStr, "g");
}

const log = true;
function logReplace(currentFilePath, mappedImportPathRelative) {
  if (log)
console.log(
  "current processed file:",
  currentFilePath,
  "; Mapped import path relative to current file:",
  mappedImportPathRelative
);
}

module.exports = replacePathAlias;

3.仅搜索并替换正则表达式

迭代所有源代码并应用正则表达式(未彻底测试):

^(import.*from\s+["|'])(${aliasesKeys})(.*)(["|'])$

,其中${aliasesKeys}包含路径别名"@"。可以通过修改第二个和第三个捕获组(路径映射+解析为相对路径)来处理新的导入路径。

此变体不能处理AST,因此可能被认为不如jcodeshift稳定。

目前,Regex仅支持导入。import "module-name"表单中的副作用导入被排除,其好处是使用搜索/替换更安全。

示例:

const path = require("path");

// here sample file content of one file as hardcoded string for simplicity.
// For your project, read all files (e.g. "fs.readFile" in node.js)
// and foreach file replace content by the return string of replaceImportPathAliases function.
const fileContentSample = `
import { AppStore } from "@/app/store/appStore-types"
import { WidgetService } from "@/app/WidgetService"
import { AppStoreImpl } from "@/app/store/AppStoreImpl"
import { rootReducer } from "@/app/store/root-reducer"
export { appStoreFactory }
`;

// corresponds to tsconfig.json paths or webpack aliases
// e.g. "@/app/store/AppStoreImpl" -> "./custom/app/path/app/store/AppStoreImpl"
const pathMappingSample = {
  "@": "./src",
  foo: "bar"
};

const currentFilePathSample = "./src/sub/a.js";

function replaceImportPathAliases(currentFilePath, fileContent, pathMap) {
  const regex = createRegex(pathMap);
  return fileContent.replace(regex, replacer);

  function replacer(_, g1, aliasGrp, restPathGrp, g4) {
    const mappedImportPath = pathMap[aliasGrp] + restPathGrp;

    let mappedImportPathRelative = path.posix.relative(
      path.dirname(currentFilePath),
      mappedImportPath
    );
    // append "./" to make it a relative import path
    if (!mappedImportPathRelative.startsWith("../")) {
      mappedImportPathRelative = `./${mappedImportPathRelative}`;
    }
    return g1 + mappedImportPathRelative + g4;
  }
}

function createRegex(pathMap) {
  const mapKeysStr = Object.keys(pathMap).reduce((acc, cur) => `${acc}|${cur}`);
  const regexStr = `^(import.*from\s+["|'])(${mapKeysStr})(.*)(["|'])$`;
  return new RegExp(regexStr, "gm");
}

console.log(
  replaceImportPathAliases(
    currentFilePathSample,
    fileContentSample,
    pathMappingSample
  )
);

这篇关于将别名@Imports重构为相对路径的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

Update another component when Formik form changes(当Formik表单更改时更新另一个组件)
Formik validation isSubmitting / isValidating not getting set to true(Formik验证正在提交/isValiating未设置为True)
React Validation Max Range Using Formik(使用Formik的Reaction验证最大范围)
Validation using Yup to check string or number length(使用YUP检查字符串或数字长度的验证)
Updating initialValues prop on Formik Form does not update input value(更新Formik表单上的初始值属性不会更新输入值)
password validation with yup and formik(使用YUP和Formick进行密码验证)