gulp-path-alias
Version:
134 lines (133 loc) • 4.91 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
const path_1 = __importDefault(require("path"));
const through2_1 = __importDefault(require("through2"));
const replacestream_1 = __importDefault(require("replacestream"));
const slash_1 = __importDefault(require("slash"));
const prefixPatternMap = {
js: 'import\\s*[^\'"]*\\(?|from|require\\s*\\(',
// poster: wxml
xml: 'src=|url=|poster=|href=',
css: '@import\\s*|url\\s*\\(',
};
/* 全匹配的正则规则 */
const exactMatchPattern = /\$$/;
/**
* 编码别名替换项
* eg. '/' -> '\\/'
*/
function encodeAliasString(alias) {
return alias.replace('/', '\\/');
}
/* 获取匹配前缀正则字符串 */
function getPrefixPatternString(prefixPatten) {
return `(?:(${prefixPatten})\\s*['"]?\\s*)`;
}
/* 获取匹配后缀正则字符串 */
function getSuffixPatternString(exactMatch = false) {
const suffixPattern = '[\'"]|\\s*\\)';
// 全匹配时,后面没有内容,拿掉斜线
return exactMatch ? suffixPattern : `\\/|${suffixPattern}`;
}
/* 获取匹配主体和后缀的正则字符串 */
function getRemainPatternString(aliasKey, exactMatch) {
const _aliasKey = exactMatch ? aliasKey.replace(exactMatchPattern, '') : aliasKey;
return `(?<alias>${_aliasKey})(${getSuffixPatternString(exactMatch)})`;
}
/* 获取相对路径 */
function relative(from, to) {
const relativePath = slash_1.default(path_1.default.relative(from, to));
if (!relativePath) {
return '.';
}
return !/^\./.test(relativePath) ? `./${relativePath}` : relativePath;
}
// 100000 rows * 100 columns -> 248ms
function replaceAll(file, dirname, aliasList) {
const ext = path_1.default.extname(file.relative);
const isStream = file.isStream();
/* 根据后缀名获得前缀正则字符串,降低复杂度 */
let prefixPatternString;
switch (ext) {
// js
case '.js':
case '.ts':
case '.wxs':
prefixPatternString = getPrefixPatternString(prefixPatternMap.js);
break;
// css
case '.css':
case '.less':
case '.scss':
case '.sass':
case '.styl':
case '.stylus':
case '.wxss':
prefixPatternString = getPrefixPatternString(prefixPatternMap.css);
break;
// xml
case '.html':
case '.wxml':
prefixPatternString = getPrefixPatternString(prefixPatternMap.xml);
break;
case '.jsx':
case '.tsx':
default:
prefixPatternString = getPrefixPatternString(Object.keys(prefixPatternMap)
.map((k) => prefixPatternMap[k])
.join('|'));
break;
}
aliasList.forEach(({ aliasKey, aliasValue }) => {
const isExactMatch = exactMatchPattern.test(aliasKey);
const remainPatternString = getRemainPatternString(aliasKey, isExactMatch);
const sentenceReg = new RegExp(`${prefixPatternString}${remainPatternString}`, 'gm');
const replacer = (match, ...args) => {
const group = args.pop();
const { alias } = group;
/* 如果替换路径是相对路径不使用 relative 路径替换,而是直接替换 */
const replaceStr = path_1.default.isAbsolute(aliasValue) ? relative(dirname, aliasValue) : aliasValue;
return match.replace(alias, replaceStr);
};
/* 先确定文件中匹配的语句,再替换其中所有匹配的 alias */
if (isStream) {
file.contents = file.contents.pipe(replacestream_1.default(sentenceReg, replacer));
}
else {
file.contents = Buffer.from(String(file.contents).replace(sentenceReg, replacer));
}
});
return file;
}
/**
*
* @param options
*/
function alias(options = {}) {
const _options = Object.assign({ cwd: process.cwd(), paths: {} }, options);
const { paths } = _options;
const isEmptyAlias = !Object.keys(paths).length;
/* 初始化 aliasList,全局使用,避免重复计算 */
const aliasList = Object.keys(paths)
/* 全匹配的需要优先替换,放在前面 */
.sort((a) => (exactMatchPattern.test(a) ? -1 : 1))
.map((aliasKey) => {
/* 替换斜线,否则生成 pattern 时会出错 */
const encodeKey = encodeAliasString(aliasKey);
return {
aliasKey: encodeKey,
aliasValue: paths[aliasKey],
};
});
return through2_1.default.obj(function (file, _, cb) {
const dirname = path_1.default.dirname(file.path);
if (file.isNull() || isEmptyAlias) {
return cb(null, file);
}
file = replaceAll(file, dirname, aliasList);
cb(null, file);
});
}
module.exports = alias;