oak-cli/config/babel-plugin/oakI18n.js

242 lines
10 KiB
JavaScript
Raw Permalink 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.

const fs = require('fs');
const { relative, resolve, join, dirname } = require('path');
const t = require('@babel/types');
const assert = require('assert');
const { fork } = require('child_process');
const Regex =
/([\\/]*[a-zA-Z0-9_-\w\W]|[\\/]*[a-zA-Z0-9_-\w\W]:)*[\\/](lib|src|es)([\\/]*[a-zA-Z0-9_-])*[\\/](pages|components)+[\\/]/;
const ModuleDict = {};
const ReactNativeProjectDict = {};
const ProcessRecord = {};
function parseFileModuleAndNs(cwd, filename) {
let cwd2 = cwd;
if (cwd.endsWith('native')) {
// react-native环境需要重新定位一下项目根目录
if (ReactNativeProjectDict.hasOwnProperty(cwd)) {
if (ReactNativeProjectDict[cwd]) {
cwd2 = join(cwd, '..');
}
}
else {
const nodeModulePath = join(cwd, '..', 'node_modules');
const packageJsonPath = join(cwd, '..', 'package.json');
if (fs.existsSync(packageJsonPath) && fs.existsSync(nodeModulePath)) {
ReactNativeProjectDict[cwd] = true;
cwd2 = join(cwd, '..');
}
else {
ReactNativeProjectDict[cwd] = false;
}
}
}
const relativePath = relative(cwd2, filename);
if (relativePath.startsWith('node_modules') || relativePath.startsWith('..')) { // 在测试环境下是相对路径
// 寻找离filename最近层的package.json看作项目名称
// 用relativePath来递进查找在操作系统层面更安全
const fileDirPaths = relativePath.replace(/\\/g, '/').split('/');
let iter = fileDirPaths.length;
let filePrjRootPath = '';
let moduleName = '';
do {
filePrjRootPath = join(cwd2, ...fileDirPaths.slice(0, iter));
const pkgJsonPath = join(filePrjRootPath, 'package.json');
if (fs.existsSync(pkgJsonPath)) {
moduleName = ModuleDict[filePrjRootPath];
if (!moduleName) {
const { name } = require(pkgJsonPath);
moduleName = name;
ModuleDict[filePrjRootPath] = name;
}
break;
}
else {
assert(iter > 0);
iter--;
}
} while (true);
// 引用的第三方项目目前合法的只有components下的组件pages是容一些较早的遗留代码
const fileRelPathInPrj = relative(filePrjRootPath, filename).replace(/\\/g, '/').split('/');;
let ns;
switch (fileRelPathInPrj[1]) {
case 'pages': {
ns = `${moduleName}-p-${fileRelPathInPrj.slice(2, fileRelPathInPrj.length - 1).join('-')}`;
break;
}
default: {
if (fileRelPathInPrj[1] !== 'components') {
assert(false);
}
assert(fileRelPathInPrj[1] === 'components', fileRelPathInPrj.join('//'));
ns = `${moduleName}-c-${fileRelPathInPrj.slice(2, fileRelPathInPrj.length - 1).join('-')}`;
break;
}
}
return {
moduleName,
ns,
};
}
else {
let moduleName = ModuleDict['./'];
if (!moduleName) {
const { name } = require(join(cwd2, 'package.json'));
ModuleDict['./'] = name;
moduleName = name;
}
const rel2paths = relative(cwd2, filename)
.replace(/\\/g, '/')
.split('/');
let ns;
switch (rel2paths[1]) {
case 'pages': {
ns = `${moduleName}-p-${rel2paths.slice(2, rel2paths.length - 1).join('-')}`;
break;
}
case 'components': {
ns = `${moduleName}-c-${rel2paths.slice(2, rel2paths.length - 1).join('-')}`;
break;
}
default: {
// 处理web/wechatMp中的数据
assert(rel2paths[1] === 'src');
const p1 = rel2paths[0];
if (p1 === 'web') {
ns = `${moduleName}-w-${rel2paths.slice(2, rel2paths.length - 1).join('-')}`;
}
else if (p1 === 'wechatMp') {
ns = `${moduleName}-wmp-${rel2paths.slice(2, rel2paths.length - 1).join('-')}`;
}
else {
assert(p1.startsWith('wechatMp'));
const iter = parseInt(p1.slice(8), 10);
ns = `${moduleName}-wmp${iter}-${rel2paths.slice(2, rel2paths.length - 1).join('-')}`;
}
break;
}
}
return {
moduleName,
ns,
};
}
}
const oakNsPropName = '#oakNamespace';
const oakModulePropName = '#oakModule';
module.exports = (babel) => {
return {
visitor: {
CallExpression(path, state) {
const { cwd, filename } = state;
const res = resolve(cwd, filename).replace(/\\/g, '/');
// this.props.t/this.t/t
// 处理策略为给第二个参数中加上'#oakNameSpace, #oakModule两个参数告知t模块此文件相应的位置再加以处理寻找
if (
/(pages|components|namespaces)[\w|\W]+(.tsx|.ts|.jsx|.js)$/.test(res)
) {
const { node } = path;
if (
node &&
node.callee &&
((t.isIdentifier(node.callee) &&
node.callee.name === 't') ||
(t.isMemberExpression(node.callee) &&
t.isIdentifier(node.callee.property) &&
node.callee.property.name === 't'))
) {
const { moduleName, ns } = parseFileModuleAndNs(cwd, filename);
const arguments = node.arguments;
// react-native会调用两次这里要保护一下
// 代码看上去可以避免被调用两次rn待测试 by Xc 20240108
const [arg0, arg1] = arguments;
assert(arg0);
if (arg1) {
// 一般是对象,也可能是变量,表达式不予考虑
// 加上Object.assign({ '#oakNamespace': xxx, '#oakModule': xxx }, arg1);
if (t.isCallExpression(arg1) && t.isMemberExpression(arg1.name) && t.isIdentifier(arg1.name.object)
&& arg1.name.object.name === 'Object' && t.isIdentifier(arg1.name.property) && arg1.name.property.name === 'assign') {
// 不处理,这里似乎会被反复调用,不知道为什么
}
else {
arguments.splice(1, 1, t.callExpression(
t.memberExpression(
t.identifier('Object'),
t.identifier('assign')
),
[
t.objectExpression(
[
t.objectProperty(t.stringLiteral(oakNsPropName), t.stringLiteral(ns)),
t.objectProperty(t.stringLiteral(oakModulePropName), t.stringLiteral(moduleName))
]
),
arg1
]
));
}
/* if (t.isObjectExpression(arg1)) {
const { properties } = arg1;
const oakNsProp = properties.find(
ele => t.isStringLiteral(ele.key) && ele.key.value === oakNsPropName
);
if (!oakNsProp) {
properties.push(
t.objectProperty(t.stringLiteral(oakNsPropName), t.stringLiteral(ns)),
t.objectProperty(t.stringLiteral(oakModulePropName), t.stringLiteral(moduleName))
);
}
}
else if (t.isIdentifier(arg1)) {
arguments.splice(1, 1, t.callExpression(
t.memberExpression(
t.identifier('Object'),
t.identifier('assign')
),
[
arg1,
t.objectExpression(
[
t.objectProperty(t.stringLiteral(oakNsPropName), t.stringLiteral(ns)),
t.objectProperty(t.stringLiteral(oakModulePropName), t.stringLiteral(moduleName))
]
)
]
));
}
else {
// 不处理,这里似乎会反复调用,不知道为什么
} */
}
else {
// 如果无参数就构造一个对象传入
arguments.push(
t.objectExpression(
[
t.objectProperty(t.stringLiteral(oakNsPropName), t.stringLiteral(ns)),
t.objectProperty(t.stringLiteral(oakModulePropName), t.stringLiteral(moduleName))
]
)
);
}
}
}
},
},
};
};