oak-cli/config/loaders/wxml-loader.js

224 lines
6.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
*
* @param {*} content 文件信息
*/
const { DOMParser, XMLSerializer } = require('@xmldom/xmldom');
const { resolve, relative } = require('path');
const { isUrlRequest, urlToRequest } = require('loader-utils');
const BOOLEAN_ATTRS = [
'wx:else',
'show-info',
'active',
'controls',
'danmu-btn',
'enable-danmu',
'autoplay',
'disabled',
'show-value',
'checked',
'scroll-x',
'scroll-y',
'auto-focus',
'focus',
'auto-height',
'password',
'indicator-dots',
'report-submit',
'hidden',
'plain',
'loading',
'redirect',
'loop',
'controls',
];
const OPERATORS = {
'&lt;': '<',
'&lte;': '<=',
'&gt;': '>',
'&gte;': '>=',
'&amp;': '&',
'&quot;': "'",
};
// 替换xmldom生成的无值属性
function replaceBooleanAttr(code) {
let reg;
BOOLEAN_ATTRS.forEach((v) => {
reg = new RegExp(`${v}=['"]${v}['"]`, 'ig');
code = code.replace(reg, v);
});
return code;
}
// 替换xmldom生成的运算符转义
function replaceOperatorAttr(code) {
let reg;
Object.keys(OPERATORS).forEach((v) => {
reg = new RegExp(`${v}`, 'ig');
code = code.replace(reg, OPERATORS[v]);
});
return code;
}
function traverse(doc, callback) {
callback(doc);
if (doc.childNodes) {
const { length } = doc.childNodes;
for (let i = 0; i < length; i++) {
traverse(doc.childNodes.item(i), callback);
}
}
}
const isSrc = (name) => name === 'src';
const isDynamicSrc = (src) => /\{\{/.test(src);
const TranslationFunction = 't';
const I18nModuleName = 'i18n';
const CURRENT_LOCALE_KEY = '$_locale';
const LOCALE_CHANGE_HANDLER_NAME = '$_localeChange';
const COMMON_LOCALE_DATA = '$_common_translations';
const CURRENT_LOCALE_DATA = '$_translations';
const WXS_PATH = 'i18n/locales.wxs';
function existsT(str) {
if (!str) return false;
// if (str.indexOf('t(') === -1) return false;
// console.log(str.substring(str.indexOf('t(') - 1, str.indexOf('t(')));
return (
str.indexOf('t(') !== -1 &&
!/^[A-Za-z0-9]*$/.test(
str.substring(str.indexOf('t(') - 1, str.indexOf('t('))
)
);
}
function replaceT(str) {
return str.replace(/t\(/g, 'i18n.t(');
}
module.exports = async function (content) {
// loader的缓存功能
// this.cacheable && this.cacheable();
const options = this.getOptions() || {}; //获取配置参数
const { context: context2 } = options;
const callback = this.async();
const { options: webpackLegacyOptions, _module = {}, _compilation = {}, resourcePath } = this;
const { context, target } = webpackLegacyOptions || this;
// console.log(context, target);
const issuer = _compilation.moduleGraph.getIssuer(this._module);
const issuerContext = (issuer && issuer.context) || context;
const root = resolve(context, issuerContext);
let source = content;
if (existsT(source)) {
const relativePath = relative(context, context2 + '/' + WXS_PATH);
source = `<wxs src='${relativePath}' module='${I18nModuleName}'></wxs>` + source;
}
if (/pages/.test(context)) {
source =
source +
'<message show="{{!!oakError}}" type="{{oakError.type}}" content="{{oakError.msg}}" />';
}
// console.log(content, options);
/**
* domparser会自动给没有value的attribute赋上值目前改不动
*/
const doc = new DOMParser({
errorHandler: {
warning(x) {
if (
x.indexOf('missed value!!') === -1 &&
x.indexOf('missed quot(")!') === -1
) {
console.log(x);
}
},
},
}).parseFromString(source, 'text/xml');
const requests = [];
traverse(doc, (node) => {
if (node.nodeType === node.ELEMENT_NODE) {
// xml存在src path路径
if (node.hasAttribute('src')) {
const value = node.getAttribute('src');
if (
value &&
!isDynamicSrc(value) &&
isUrlRequest(value, root)
) {
const path = resolve(root, value)
// const request = urlToRequest(value, root);
requests.push(path);
}
}
// 处理oak:value声明的属性
if (node.hasAttribute('oak:value')) {
const oakValue = node.getAttribute('oak:value');
node.removeAttribute('oak:value');
node.setAttribute('value', `{{${oakValue}}}`);
node.setAttribute('data-attr', oakValue);
node.setAttribute('oakPath', oakValue);
// node.setAttribute('oakValue', `{{${oakValue}}}`);
node.setAttribute('oakParent', `{{oakFullpath}}`);
if (node.hasAttribute('oak:forbidFocus')) {
node.removeAttribute('oak:forbidFocus');
} else {
node.setAttribute('focus', `{{!!oakFocused.${oakValue}}}`);
}
}
}
if (node.nodeType === node.TEXT_NODE) {
// 处理i18n 把t()转成i18n.t()
if (existsT(node.nodeValue)) {
const val = replaceT(node.nodeValue);
const valArr = val.split('}}');
let newVal = '';
valArr.forEach((ele, index) => {
if (existsT(ele)) {
const head = ele.substring(0, ele.indexOf(')'));
const end = ele.substring(ele.indexOf(')'));
newVal += head + `,${CURRENT_LOCALE_KEY},${CURRENT_LOCALE_DATA} || ''` + end + '}}';
} else if (ele) {
newVal += ele + '}}';
}
})
node.deleteData(0, node.nodeValue.length);
node.insertData(0, newVal);
}
}
});
const loadModule = (request) =>
new Promise((resolve, reject) => {
this.addDependency(request);
this.loadModule(request, (err, src) => {
/* istanbul ignore if */
if (err) {
reject(err);
} else {
resolve(src);
}
});
});
try {
for (const req of requests) {
const module = await loadModule(req);
}
let code = new XMLSerializer().serializeToString(doc);
code = replaceBooleanAttr(code);
code = replaceOperatorAttr(code);
callback(null, code);
} catch (err) {
callback(err, content);
}
};