177 lines
5.6 KiB
JavaScript
177 lines
5.6 KiB
JavaScript
const fs = require('fs');
|
||
const path = require('path');
|
||
const { icons } = require('./svg.json');
|
||
// 待替换图标名称,和目标替换图标名称,映射关系
|
||
const ICON_MAP = require('./icon-map');
|
||
|
||
const PLUGIN_NAME = 't-icon-replace-webpack-plugin';
|
||
class TIconReplaceWebpackPlugin {
|
||
constructor(options) {
|
||
this.options = {
|
||
name: PLUGIN_NAME,
|
||
staticPath: 'static',
|
||
debug: false,
|
||
rules: [
|
||
/**
|
||
* 替换按需加载的icon组件
|
||
*/
|
||
{
|
||
test: /tdesign-icons-vue\/(esm|lib)\/components\/.*\.js$/,
|
||
modify: (src, pathString) => {
|
||
let newData = src || '';
|
||
// 获取需要替换的icon组件
|
||
const fileNameObj = pathString.split('/');
|
||
const fileNameExt = fileNameObj[fileNameObj.length - 1];
|
||
const fileName = fileNameExt.split('.')[0];
|
||
const module = ICON_MAP[fileName];
|
||
// console.log('newData', newData);
|
||
if (module) {
|
||
newData = this.replace(src);
|
||
}
|
||
|
||
return newData;
|
||
},
|
||
},
|
||
/**
|
||
* 替换全量t-icon组件
|
||
*/
|
||
{
|
||
test: /tdesign-icons-vue\/(esm|dist)\/svg-sprite\/svg-sprite\.js$/,
|
||
modify: (src) => {
|
||
const str =
|
||
process.env.RUN_TYPE === 'demo'
|
||
? '//tencent-tdgv-1255000078.cos.zjywxc.csp.xc01.cloud.sat.tax/icon/svg.js'
|
||
: /**
|
||
* 本地开发模式会编译成绝对路径,如/dev/static/svg.js
|
||
* 做成物料包会编译成./static/svg.js
|
||
*/
|
||
`${process.env.NODE_ENV === 'production' ? '.' : process.env.VUE_APP_CDN_PATH}/${
|
||
this.options.staticPath
|
||
}/svg.js`;
|
||
// console.log('str', str);
|
||
return src.replace(/(CDN_ICONFONT_URL = ['|"]).*(['|"])/, `$1${str}$2`);
|
||
},
|
||
},
|
||
],
|
||
...options,
|
||
};
|
||
|
||
// 如果不是用于demo站点,则把svg.js拷贝到工程目录去
|
||
if (
|
||
process.env.RUN_TYPE !== 'demo' &&
|
||
// 确保跑在vue-cli下
|
||
process.env.VUE_APP_ENV !== undefined &&
|
||
// 非lint模式
|
||
/vue-cli-service lint/.test(process.env.npm_lifecycle_script) !== true
|
||
) {
|
||
this.copyJs();
|
||
}
|
||
|
||
// return this.replacePlugin();
|
||
}
|
||
|
||
apply(compiler) {
|
||
compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
|
||
const modifiedModules = [];
|
||
const { rules, debug } = this.options;
|
||
const tapCallback = (params, normalModule) => {
|
||
const userRequest = normalModule.userRequest || '';
|
||
|
||
const startIndex = userRequest.lastIndexOf('!') === -1 ? 0 : userRequest.lastIndexOf('!') + 1;
|
||
|
||
const moduleRequest = userRequest.substr(startIndex).replace(/\\/g, '/');
|
||
|
||
if (modifiedModules.includes(moduleRequest)) {
|
||
return;
|
||
}
|
||
|
||
rules.forEach((options, ruleIndex) => {
|
||
const { test, modify } = options;
|
||
const isMatched = (() => {
|
||
if (typeof test === 'function' && test(normalModule)) {
|
||
return true;
|
||
}
|
||
|
||
return test instanceof RegExp && test.test(moduleRequest);
|
||
})();
|
||
|
||
if (debug && isMatched) {
|
||
// eslint-disable-next-line no-console
|
||
console.log(`[${PLUGIN_NAME}] Add loader for module ${moduleRequest} at index ${ruleIndex}.`);
|
||
}
|
||
|
||
if (isMatched) {
|
||
normalModule.loaders.push({
|
||
loader: require.resolve('./loader.js'),
|
||
options: {
|
||
path: moduleRequest,
|
||
ruleIndex,
|
||
modify,
|
||
},
|
||
});
|
||
|
||
modifiedModules.push(moduleRequest);
|
||
}
|
||
});
|
||
};
|
||
compilation.hooks.normalModuleLoader.tap(PLUGIN_NAME, tapCallback);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 替换单个icon js文件的核心处理函数
|
||
* @param source
|
||
* @returns {*}
|
||
*/
|
||
replace(source = '') {
|
||
let newSource = source;
|
||
// let iconName = source.match(/-icon-([\w|-]+)/g)[0];
|
||
const iconNameMatch = /id:[ ]?'([\w|-]+)'/g.exec(source);
|
||
if (iconNameMatch && iconNameMatch.length > 1) {
|
||
const iconName = iconNameMatch[1];
|
||
const newIconName = ICON_MAP[iconName];
|
||
const newIconSvg = icons.find((t) => t.name === newIconName);
|
||
// 存在图标替换
|
||
if (newIconName && newIconSvg) {
|
||
// 匹配 svg / path 属性 进行替换
|
||
let attrs = newIconSvg.svgCode.match(/([\w-]+?=".*?")/g);
|
||
if (!attrs) {
|
||
console.warn('icon svgCode is not correct, it should like `<svg ...><path ...></path></svg>`');
|
||
return;
|
||
}
|
||
attrs = attrs.map((t) => {
|
||
const r = t.split('=');
|
||
return { key: r[0], value: r[1] };
|
||
});
|
||
// 不包含 value 的 svg 属性
|
||
const pureAttrs = attrs.map((t) => t.key);
|
||
const regExp = new RegExp(`"*(${pureAttrs.join('|')})"*: ".*?"`, 'g');
|
||
newSource = source.replace(regExp, (str) => {
|
||
for (let i = 0, len = attrs.length; i < len; i++) {
|
||
const { key, value } = attrs[i];
|
||
if (str.indexOf(key) !== -1) {
|
||
return `"${key}": ${value}`;
|
||
}
|
||
}
|
||
return str;
|
||
});
|
||
}
|
||
}
|
||
|
||
return newSource;
|
||
}
|
||
|
||
/**
|
||
* 拷贝全量svg到项目静态目录
|
||
*/
|
||
copyJs() {
|
||
fs.copyFileSync(
|
||
path.resolve(__dirname, 'svg.js'),
|
||
path.resolve(process.cwd(), `public/${this.options.staticPath}/svg.js`),
|
||
);
|
||
console.log('====>> 拷贝全量svg到项目静态目录成功');
|
||
}
|
||
}
|
||
|
||
module.exports = TIconReplaceWebpackPlugin;
|