uniapp-sfc-loader
Version:
修改自uniapp-router-view-loader,解决windows下路径问题
404 lines (343 loc) • 11.7 kB
JavaScript
import Path from 'path';
import Fs from 'fs';
import ErrorId from './errorId';
import { parse } from 'node-html-parser';
import { name, version } from '../config';
// const reg = /^node-modules\/uview-ui\S+/
/**
* 浏览器控制台输出信息
* console.log
*/
export const consoleStyle = () => {
return [
`\`\n %c ${name} V${version} `.concat(`%c url: 965.ink/uniapp-router-view-loader \n\``),
`'color: #ffffff; background: #64b587; padding:5px 0;'`,
`'color: #fff;background: #38485c; padding:5px 0; margin-left:-1px;'`
];
};
/**
* 获取 文件匹配正则(vue或nvue文件)
* @param {string} publicPath
* @param {string} path
* @returns
*/
export const getFileMatchReg = function (publicPath, path) {
const fullPath = (Path.join(publicPath, `/${path}`)).split(Path.sep).join('/');
const regStr = JSON.stringify(`^${fullPath}.(n)?vue$`);
const reg = new RegExp(regStr.substring(1, regStr.length - 1));
// const reg = new RegExp(`^${fullPath}.(n)?vue$`)
return reg;
};
/**
* 是否是 vue 或 nvue文件
* @param {string} path
* @returns
*/
export const isVueFile = function (path) {
return /.(n?)vue$/.test(path);
};
/**
* 获取 所有已注册的路由文件的正则规则(vue或nvue文件)
* @returns
*/
export const getRouteFileMatchRegAll = function (config) {
try {
const jsonStr = Fs.readFileSync(
Path.join(process.env.UNI_INPUT_DIR, './pages.json'),
'utf8'
);
// 更安全的解析方式,不使用正则替换
let pagesConfig;
try {
// 尝试直接解析
pagesConfig = JSON.parse(jsonStr);
} catch (jsonError) {
// 如果直接解析失败,尝试移除注释后再解析
// 这里采用逐行处理的方式,只移除真正的注释行
const cleanedJsonStr = jsonStr
.split('\n')
.map(line => {
// 移除行注释(排除URL中的双斜杠)
const commentIndex = line.indexOf('//');
if (commentIndex > -1) {
// 检查前面是否有引号,避免删除URL中的内容
const beforeComment = line.substring(0, commentIndex);
const quoteCount = (beforeComment.match(/"/g) || []).length;
// 如果引号数量为偶数,说明双斜杠不在字符串内
if (quoteCount % 2 === 0) {
return line.substring(0, commentIndex);
}
}
return line;
})
.join('\n');
pagesConfig = JSON.parse(cleanedJsonStr);
}
const { pages, subPackages = [] } = pagesConfig;
const list = [];
pages.forEach(({ path }) => {
list.push(getFileMatchReg(process.env.UNI_INPUT_DIR, path));
});
subPackages.forEach(({ pages, root }) => {
pages.forEach(({ path }) => {
list.push(getFileMatchReg(process.env.UNI_INPUT_DIR, root + '/' + path));
});
});
return list;
} catch (e) {
console.log(e);
print('error', ErrorId[10001]);
}
};
/**
* 添加代码到头部
* @param {*} source
* @param {*} code
*/
export const addCodeToHeader = function (source, code) {
return source.replace(/<view(.*?)>/, (s) => s + code);
};
/**
* 添加代码到尾部
* @param {*} source
* @param {*} code
*/
export const addCodeToFooter = function (source, code) {
return source.replace(
/(<\/view>)([\s]*)(<\/template>)(?!(([\s\S]*)(<\/template>)))/,
(s) => code + s
);
};
// /**
// * 获取 App.vue 文件中 template 默认内容
// * 先匹配标签再移除首位标签
// * @param {*} source
// * @returns
// */
// function handle(source) {
// return source.match(/(<template>).*?(<\/template>)/)[0].replace(/<(\/?)template>/g, '')
// }
/**
* 获取 App.vue 文件中 template 默认内容
* 先匹配标签再移除首位标签
* @param {*} source
* @returns
*/
export const handleAppTemplateAddCode = function (source) {
// `App.vue`中我们添加的代码`tepmlate`代码
let addCode = '';
switch (process.env.UNI_PLATFORM) {
case 'h5':
// 获取App.vue中uniapp插入的代码和我们添加的代码
const originTemplateCode = source
.replace(/(?<=<\/template>)[\w\W]*<\/template>/, (s) => {
addCode = s;
return '';
})
.match(/<template>[\s\S]+<\/template>/)[0];
// 移除我们添加的代码, 使代码还原
source = source.replace(/<template>[\s\S]+<\/template>/, originTemplateCode);
return {
source,
addCode
};
case 'mp-weixin':
case 'mp-alipay':
case 'mp-baidu':
case 'mp-toutiao':
case 'mp-kuaishou':
case 'mp-lark':
case 'mp-qq':
case 'mp-360':
case 'quickapp-webview':
case 'app-plus':
case 'vite':
source = source.replace(/<template>[\s\S]+<\/template>/, (s) => {
addCode = s;
return '';
});
return {
source,
addCode
};
default:
print('error', ErrorId[10201]);
}
};
/**
* 获取 template 代码中的每组闭合根标签
* 先移除template标签再匹配
* 例如:3组根闭合标签
* ```
* <text>123</text>
* <view />
* <view>
* <text>123</text>
* </view>
* ```
* @param {*} source
*/
export const handleGetTemplateRowCode = function (source) {
// 过滤template标签
source = source.replace(/<(\/?)template>/g, '');
// 过滤注释标签并解第一层级的所有标签
const codeStrList = parse(source).querySelectorAll('> *').toString().split(',');
// 过滤 \n和空格
// return codeStrList.map(codeStr => codeStr.replace(/(\n?)\s+/g, ''));
return codeStrList.map((codeStr) => codeStr.replace(/\n\s+/g, ''));
// ===
// const list = [];
// // 过滤 注释标签
// source = parse(source).toString();
// // <Label />
// source = source.replace(/<.*? \/>/g, s => {
// list.push(s);
// return '';
// })
// source.match(/(<.*? \/>)|(<.*?>([\w\s<>\/]+)<\/.*?>)/g)
// return list;
};
/**
* 获取 App.vue代码中实际添加到页面代码的头部或尾部标签
* @param {*} labelList
* @returns
*/
export const handleGetTemplateHeaderOrFooterLabelCode = function (labelList) {
const viewRouterReg = /<view-router(\s{0,})><\/view-router>/;
const header = [];
const footer = [];
let flag = false;
let count = 0;
labelList.forEach((label) => {
if (viewRouterReg.test(label)) {
flag = true;
count++;
return true;
}
if (flag) {
footer.push(label);
} else {
header.push(label);
}
});
// 标签异常
if (count === 0) {
print('error', ErrorId[10101]);
} else if (count > 1) {
print('error', ErrorId[10102]);
}
return {
header,
footer
};
};
/**
* 获取 显示ID
* @returns
*/
export const getPrintID = function () {
return `[${name}]:`;
};
/**
* 打印日志
* @param {'log'|'error'} type
* @param {string} msg
*/
export const print = function (type = 'log', msg) {
const ID = getPrintID();
switch (type) {
case 'log':
console.log(`${ID}${msg}`);
break;
case 'error':
throw new Error(`${ID}${msg}`);
}
};
/**
* 获取 ast 中静态和动态 class 内容
* @param {*} ast
* @returns
*/
// export const getClass = function (ast) {
// const staticName = ast.attrsMap['class'];
// const dynamicName = ast.attrsMap[':class'];
// return {
// hasStatic: staticName !== undefined,
// hasDynamic: dynamicName !== undefined,
// staticName,
// dynamicName
// }
// }
/**
* 处理 class 拼接
* @param {string} source
* @param {Object} ast
* @param {Object} ast.attrsMap
* @param {string} ast.attrsMap.class
* @returns
*/
// export const handleClassJoin = function (source, ast) {
// const classCode = getClass(ast)
// // const static = / class=('|").*?('|")/;
// const static = /\sclass=('|").*?('|")/;
// const hasStaticReg = /^<\w+(.*?)(([\s\S]*?)class=('|").*?('|"))/;
// // const dynamic = / :class="('?).*?('?)"/
// const dynamic = /\s:class="('?).*?('?)"/
// const hasDynamicReg = /^<\w+(.*?)(([\s\S]*?):class="('?).*?('?)")/
// let res = source
// // 静态 class
// // class='name' 或 :class="'name'"
// if (classCode.hasStatic && hasStaticReg.test(source) !== null) {
// const className = classCode.staticName + ' ' + Config.WX_THEME_CLASS_KEY;
// console.log('===== className =======')
// console.log(className)
// console.log('===== className =======')
// res = source.replace(static, ` class="${className}"`)
// console.log('======== handle source =========')
// console.log(res)
// console.log('======== handle source =========')
// }
// // 动态 class
// // :class="nameList" 或 :class="nameList + ' name'" 或 :class="[nameList, 'name']"
// else if (classCode.hasDynamic && hasDynamicReg.test(source) !== null) {
// const className = classCode.dynamicName.replace(/\[|\]/g, '') + `, '${Config.WX_THEME_CLASS_KEY}'`;
// res = source.replace(dynamic, ` :class="[${className}]"`)
// }
// // AST 匹配失败
// else {
// return `<view style="color:red;">${JSON.stringify(ast.attrsMap)}</view>`;
// }
// return res;
// }
// /**
// * 替换 VNode 虚拟标签,例:"<VNode-Navbar />"
// * @param {string} source
// */
// export const handleVNodeReplace = function (source) {
// Object.keys(Config.VNode).forEach((name) => {
// const reg = new RegExp(`<${name}(\\s{0,})\/>`);
// if (reg.test(source)) {
// const template = Fs.readFileSync(
// Path.join(__dirname, '../', Config.VNode[name])
// ).toString();
// source = source.replace(reg, template);
// }
// });
// return source;
// };
/**
* 虚拟标签,例:"<div />" => "<view />"
* @param {string} source
*/
export const handleVLabelReplacement = function (source, vLabel = {}) {
for (const [key, val] of Object.entries(vLabel)) {
// "<u-text>xxx</u-text>"
const reg1 = new RegExp(`<(\/?)${key}(\\s*)>`, 'g');
// "<u-text />"
const reg2 = new RegExp(`<${key}(.*?)\/>`, 'g');
const labelReplaceReg = new RegExp(key, 'g');
source = source.replace(reg1, (s) => s.replace(labelReplaceReg, val));
source = source.replace(reg2, (s) => s.replace(labelReplaceReg, val));
}
return source;
};