t-comm
Version:
专业、稳定、纯粹的工具库
241 lines (238 loc) • 8.76 kB
JavaScript
import { getFs } from '../nodejs/fs.mjs';
import { getPath } from '../nodejs/path.mjs';
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
var DEFAULT_EXTENSIONS = ['.vue', '.js', '.ts', '.less', '.css', '.scss'];
/**
* 递归获取目录下所有匹配扩展名的文件
* @example
* ```ts
* // 默认扫描 .vue/.js/.ts/.less/.css/.scss
* const files = getAllFiles('/path/to/src');
*
* // 自定义扫描扩展名
* const tsFiles = getAllFiles('/path/to/src', ['.ts', '.tsx']);
* ```
*/
function getAllFiles(dirPath) {
var extensions = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_EXTENSIONS;
var fileList = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
var fs = getFs();
if (!fs.existsSync(dirPath)) return fileList;
var files = fs.readdirSync(dirPath);
var _iterator = _createForOfIteratorHelper(files),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var file = _step.value;
var filePath = getPath().join(dirPath, file);
var stat = fs.statSync(filePath);
if (stat.isDirectory()) {
getAllFiles(filePath, extensions, fileList);
} else if (extensions.includes(getPath().extname(filePath))) {
fileList.push(filePath);
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
return fileList;
}
/**
* 将 alias 路径转换为相对路径
* @example
* ```ts
* toRelativePath(
* '/proj/src/views/Home/index.vue',
* 'src',
* 'components/Button/index.vue',
* '/proj',
* { src: 'src' },
* );
* // '../../components/Button/index.vue'
* ```
*/
function toRelativePath(filePath, alias, subPath, rootDir, aliasMap) {
var path = getPath();
var targetDir = aliasMap[alias];
var targetAbsPath = path.resolve(rootDir, targetDir, subPath);
var fileDir = path.dirname(filePath);
var relativePath = path.relative(fileDir, targetAbsPath);
if (!relativePath.startsWith('.')) {
relativePath = "./".concat(relativePath);
}
relativePath = relativePath.split(path.sep).join('/');
return relativePath;
}
/**
* 替换文件内容中单个 alias 的引入为相对路径
*/
function replaceContentForAlias(content, alias, filePath, rootDir, aliasMap) {
var escapedAlias = alias.replace(/[.*+?^${}()|[\\]\\]/g, '\\$&');
var regex = new RegExp("(['\"])".concat(escapedAlias, "/([^'\"]+)(['\"])"), 'g');
var replaced = false;
var newContent = content.replace(regex, function (match, quote1, sub, quote2) {
var rel = toRelativePath(filePath, alias, sub, rootDir, aliasMap);
replaced = true;
return "".concat(quote1).concat(rel).concat(quote2);
});
return {
content: newContent,
replaced: replaced
};
}
/**
* 替换单个文件中的 alias 引入为相对路径
* @example
* ```ts
* // 假设文件内原本写的是 `from 'src/components/Btn.vue'`
* // 调用后会被改写为 `from '../../components/Btn.vue'`
* const result = replaceAliasInFile(
* '/proj/src/views/Home/index.vue',
* '/proj',
* { src: 'src' },
* );
* // { replaced: true, error: null }
* ```
*/
function replaceAliasInFile(filePath, rootDir, aliasMap) {
try {
var content = getFs().readFileSync(filePath, 'utf-8');
var hasReplaced = false;
for (var _i = 0, _Object$keys = Object.keys(aliasMap); _i < _Object$keys.length; _i++) {
var alias = _Object$keys[_i];
var result = replaceContentForAlias(content, alias, filePath, rootDir, aliasMap);
content = result.content;
if (result.replaced) {
hasReplaced = true;
}
}
if (hasReplaced) {
getFs().writeFileSync(filePath, content, 'utf-8');
}
return {
replaced: hasReplaced,
error: null
};
} catch (err) {
return {
replaced: false,
error: err.message
};
}
}
/**
* 收集所有需要扫描的文件列表
* @example
* ```ts
* const files = collectFiles({
* rootDir: '/proj',
* aliasMap: { src: 'src' },
* scanDirs: ['src/views', 'src/components'],
* scanRootFiles: ['vite.config.ts'],
* });
* ```
*/
function collectFiles(options) {
var rootDir = options.rootDir,
scanDirs = options.scanDirs,
_options$scanRootFile = options.scanRootFiles,
scanRootFiles = _options$scanRootFile === void 0 ? [] : _options$scanRootFile,
_options$supportedExt = options.supportedExtensions,
supportedExtensions = _options$supportedExt === void 0 ? DEFAULT_EXTENSIONS : _options$supportedExt;
var allFiles = [];
var _iterator2 = _createForOfIteratorHelper(scanDirs),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var dir = _step2.value;
var dirPath = getPath().resolve(rootDir, dir);
getAllFiles(dirPath, supportedExtensions, allFiles);
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
var _iterator3 = _createForOfIteratorHelper(scanRootFiles),
_step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var file = _step3.value;
var filePath = getPath().resolve(rootDir, file);
if (getFs().existsSync(filePath)) {
allFiles.push(filePath);
}
}
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
}
return allFiles;
}
/**
* 执行 alias 路径替换
* @param options - 替换配置
* @returns 替换统计信息
* @example
* ```ts
* const { replacedCount, errorCount } = replaceAlias({
* rootDir: '/proj',
* aliasMap: {
* src: 'src',
* '@': 'src',
* },
* scanDirs: ['src'],
* scanRootFiles: ['vite.config.ts', 'tsconfig.json'],
* supportedExtensions: ['.ts', '.vue', '.scss'],
* });
* console.log(`替换了 ${replacedCount} 个文件,${errorCount} 个出错`);
* ```
*/
function replaceAlias(options) {
var rootDir = options.rootDir,
aliasMap = options.aliasMap;
var allFiles = collectFiles(options);
console.log('🔄 开始替换 alias 路径为相对路径...\n');
var replacedCount = 0;
var errorCount = 0;
var _iterator4 = _createForOfIteratorHelper(allFiles),
_step4;
try {
for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
var filePath = _step4.value;
var _replaceAliasInFile = replaceAliasInFile(filePath, rootDir, aliasMap),
replaced = _replaceAliasInFile.replaced,
error = _replaceAliasInFile.error;
var relativePath = getPath().relative(rootDir, filePath);
if (error) {
console.log(" \u26A0\uFE0F ".concat(relativePath, " (").concat(error, ")"));
errorCount += 1;
} else if (replaced) {
console.log(" \u2705 ".concat(relativePath));
replacedCount += 1;
}
}
} catch (err) {
_iterator4.e(err);
} finally {
_iterator4.f();
}
if (replacedCount === 0 && errorCount === 0) {
console.log(' ℹ️ 没有找到需要替换的 alias 路径');
} else {
console.log("\n\u2728 \u5171\u66FF\u6362\u4E86 ".concat(replacedCount, " \u4E2A\u6587\u4EF6\u4E2D\u7684 alias \u8DEF\u5F84"));
if (errorCount > 0) {
console.log("\u26A0\uFE0F ".concat(errorCount, " \u4E2A\u6587\u4EF6\u5904\u7406\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u6743\u9650"));
}
}
return {
replacedCount: replacedCount,
errorCount: errorCount
};
}
export { collectFiles, getAllFiles, replaceAlias, replaceAliasInFile, toRelativePath };