|
|
|
const fs = require("fs");
|
|
|
|
const path = require("path");
|
|
|
|
const prettier = require("prettier");
|
|
|
|
const { exec } = require("child_process");
|
|
|
|
const { search, initDepts, depts } = require("./es6.xtype");
|
|
|
|
const lodash = require("lodash");
|
|
|
|
const DEPTS = depts;
|
|
|
|
|
|
|
|
// const XTYPE_ONLY = false;
|
|
|
|
// const THIS_REPLACE = false;
|
|
|
|
|
|
|
|
const ConflictImport = [];
|
|
|
|
const CircularDependency = [];
|
|
|
|
|
|
|
|
function objHaveFunction(obj) {
|
|
|
|
if (obj === null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Object.keys(obj).some(key => {
|
|
|
|
const value = obj[key];
|
|
|
|
if (typeof value === "object") {
|
|
|
|
return objHaveFunction(value);
|
|
|
|
} else if (typeof value === "function") {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function parserImport(code) {
|
|
|
|
const reg = /import {([\s\S]*?)} from "(.*?)";/g;
|
|
|
|
|
|
|
|
const importMap = {};
|
|
|
|
let regResult = reg.exec(code);
|
|
|
|
while (regResult) {
|
|
|
|
importMap[regResult[2]] = regResult[1]
|
|
|
|
.split(",")
|
|
|
|
.map(el => el.trim())
|
|
|
|
.filter(el => el);
|
|
|
|
regResult = reg.exec(code);
|
|
|
|
}
|
|
|
|
|
|
|
|
return importMap;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function saveAndFixCode(path, code) {
|
|
|
|
let _code = code;
|
|
|
|
if (!code) {
|
|
|
|
_code = fs.readFileSync(path).toString();
|
|
|
|
}
|
|
|
|
const prettierCode = prettier.format(_code, {
|
|
|
|
tabWidth: 4,
|
|
|
|
parser: "babel",
|
|
|
|
printWidth: 120,
|
|
|
|
});
|
|
|
|
fs.writeFileSync(path, prettierCode);
|
|
|
|
|
|
|
|
new Promise(res => {
|
|
|
|
exec(`yarn eslint --fix ${path}`, () => {
|
|
|
|
res();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function divideFile(srcName) {
|
|
|
|
const targetPath = srcName.match(/.*\//g);
|
|
|
|
const sourceCode = fs.readFileSync(srcName).toString();
|
|
|
|
const splitSourceCode = sourceCode.match(/[\.\s]{1}[^\s\.]+?\s=\sBI\.inherit\([^]*?BI\.shortcut.*\;/g);
|
|
|
|
const newFileNames = [];
|
|
|
|
for (let i = 0; i < splitSourceCode.length; i++) {
|
|
|
|
// 去除开头的 空格 或者 .
|
|
|
|
const newCode = splitSourceCode[i].slice(1);
|
|
|
|
// 匹配第一个等号之前的组件名
|
|
|
|
const componentName = /BI\.shortcut\("(.*)"/.exec(newCode)[1];
|
|
|
|
// 文件名转化 ButtonIcon => demo.button.icon.js
|
|
|
|
const demoFileName = componentName + '.js';
|
|
|
|
newFileNames.push(demoFileName);
|
|
|
|
// 代码 componentName 前面加上 BI.
|
|
|
|
const fileCode = 'BI.' + newCode;
|
|
|
|
|
|
|
|
// 规范最后一行的组件为 BI.Button
|
|
|
|
const targetComponet = /(BI\..*)\s=/.exec(fileCode)[1];
|
|
|
|
// 最后一行的内容
|
|
|
|
const replaceContext = /BI\.shortcut.*;/.exec(fileCode)[0];
|
|
|
|
// 替换
|
|
|
|
const finalCode = fileCode.replace(replaceContext,`BI.shortcut("${componentName}", ${targetComponet});`)
|
|
|
|
|
|
|
|
// 创建新文件
|
|
|
|
fs.writeFileSync(targetPath + demoFileName, finalCode);
|
|
|
|
}
|
|
|
|
return newFileNames;
|
|
|
|
}
|
|
|
|
|
|
|
|
// const target = [];
|
|
|
|
|
|
|
|
// 加载模块
|
|
|
|
const loader = {
|
|
|
|
// G: { "@/core": { shortcut: true } },
|
|
|
|
load(srcName, module) {
|
|
|
|
const G = loader.G;
|
|
|
|
// if (target.indexOf(module) >= 0) {
|
|
|
|
// G["@/core"][module] = true;
|
|
|
|
|
|
|
|
// return true;
|
|
|
|
// }
|
|
|
|
if (module.startsWith('"bi.')) {
|
|
|
|
const key = search(srcName, module);
|
|
|
|
if (key) {
|
|
|
|
if (!G[key]) {
|
|
|
|
G[key] = {};
|
|
|
|
}
|
|
|
|
const clzName = depts[module].clzName;
|
|
|
|
G[key][clzName] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return !!key;
|
|
|
|
} else {
|
|
|
|
const key = search(srcName, module);
|
|
|
|
if (key) {
|
|
|
|
if (!G[key]) {
|
|
|
|
G[key] = {};
|
|
|
|
}
|
|
|
|
G[key][module] = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return !!key;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
async function handleFile(srcName) {
|
|
|
|
await saveAndFixCode(srcName);
|
|
|
|
// 全局状态回归
|
|
|
|
let G = (loader.G = {});
|
|
|
|
|
|
|
|
const sourceCode = fs.readFileSync(srcName).toString();
|
|
|
|
if (sourceCode.match(/shortcut/g).length > 1) {
|
|
|
|
console.log('该文件存在多处BI.shorcut, 需要拆分...');
|
|
|
|
const newTargets = divideFile(srcName);
|
|
|
|
newTargets.forEach(name => console.log(name));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const result = /BI\.(.*?)\s=\sBI\.inherit\(/.exec(sourceCode);
|
|
|
|
|
|
|
|
if (!result) {
|
|
|
|
// console.log(`已经es6过,替换 xtype => ${srcName}`);
|
|
|
|
if (!/export class/.test(sourceCode)) {
|
|
|
|
// console.log("忽略文件", srcName);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 处理 xtype
|
|
|
|
const noXtypeCode = sourceCode.replace(/type:\s?"bi\.(.*?)"/g, v => {
|
|
|
|
const matchedSentence = v.replace(/type:\s?/, "");
|
|
|
|
const loadSuccess = loader.load(srcName, matchedSentence);
|
|
|
|
if (loadSuccess) {
|
|
|
|
const clzName = depts[matchedSentence].clzName;
|
|
|
|
|
|
|
|
return `type: ${clzName}.xtype`;
|
|
|
|
} else {
|
|
|
|
console.log(`xtype 替换失败 ${matchedSentence} `);
|
|
|
|
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
// 识别 import
|
|
|
|
const importMap = parserImport(noXtypeCode);
|
|
|
|
|
|
|
|
// 合并原来的 import 到 G
|
|
|
|
lodash.forEach(importMap, (depts, module) => {
|
|
|
|
depts.forEach(dept => {
|
|
|
|
if (!G[module]) {
|
|
|
|
G[module] = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
G[module][dept] = true;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 合并包
|
|
|
|
const crossPackages = fs.readdirSync("src").map(el => `@/${el}`);
|
|
|
|
lodash.forEach(G, (depts, module) => {
|
|
|
|
crossPackages.forEach(crosspackage => {
|
|
|
|
if (module.indexOf(crosspackage.replace(/^@\//, "")) >= 0) {
|
|
|
|
if (!G[crosspackage]) {
|
|
|
|
G[crosspackage] = {};
|
|
|
|
}
|
|
|
|
Object.assign(G[crosspackage], depts);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
const tmpG = {};
|
|
|
|
lodash.forEach(G, (depts, module) => {
|
|
|
|
const flag = lodash.some(
|
|
|
|
crossPackages,
|
|
|
|
crosspackage =>
|
|
|
|
module.indexOf(crosspackage) >= 0 && !module.startsWith("@"),
|
|
|
|
);
|
|
|
|
if (!flag) {
|
|
|
|
tmpG[module] = depts;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let circle = false;
|
|
|
|
// 处理循环依赖,base: ["@/case", "@/base", "@/widget"] 等于 base 不能直接引数组内的包
|
|
|
|
const forbiddenCrossRules = {
|
|
|
|
base: ["@/case", "@/base", "@/widget"],
|
|
|
|
"case": ["@/case", "@/widget"],
|
|
|
|
widget: ["@/widget"],
|
|
|
|
component: ["@/component"],
|
|
|
|
core: ["@/core", "@base", "@/widget", "@/case"],
|
|
|
|
};
|
|
|
|
|
|
|
|
const forbiddenKeys = [];
|
|
|
|
const circleG = {};
|
|
|
|
|
|
|
|
lodash.forEach(G, (depts, module) => {
|
|
|
|
// 找出 rule
|
|
|
|
const packages = Object.keys(forbiddenCrossRules);
|
|
|
|
|
|
|
|
let key = packages.filter(
|
|
|
|
_package => srcName.indexOf(_package) >= 0,
|
|
|
|
);
|
|
|
|
if (key.length !== 1) {
|
|
|
|
throw new Error(
|
|
|
|
"理论不可能出现这个问题,需要 treecat 优化下找包逻辑1",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
key = key[0];
|
|
|
|
const rule = forbiddenCrossRules[key];
|
|
|
|
|
|
|
|
if (lodash.includes(rule, module)) {
|
|
|
|
circle = true;
|
|
|
|
|
|
|
|
const deptsArr = Object.keys(depts);
|
|
|
|
if (deptsArr.filter(dept => !DEPTS[dept]).length > 0) {
|
|
|
|
throw new Error(
|
|
|
|
"理论不可能出现这个问题,需要 treecat 优化下找包逻辑2",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
deptsArr
|
|
|
|
.filter(dept => DEPTS[dept])
|
|
|
|
.forEach(dept => {
|
|
|
|
const value = `@${DEPTS[dept].replace(path.resolve("src"), "").replace(/\\/g, "/").replace(/\.js$/, "")}`;
|
|
|
|
if (!tmpG[value]) {
|
|
|
|
tmpG[value] = {};
|
|
|
|
}
|
|
|
|
tmpG[value][dept] = true;
|
|
|
|
});
|
|
|
|
forbiddenKeys.push(module);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
Object.assign(tmpG, circleG);
|
|
|
|
forbiddenKeys.forEach(key => {
|
|
|
|
delete tmpG[key];
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 较验手工 import 错误
|
|
|
|
const map = {};
|
|
|
|
let conflict = false;
|
|
|
|
|
|
|
|
lodash.forEach(tmpG, (depts, module) => {
|
|
|
|
lodash.forEach(depts, (_, _import) => {
|
|
|
|
if (map[_import] && map[_import] !== module) {
|
|
|
|
conflict = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
map[_import] = module;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
conflict && ConflictImport.push(srcName);
|
|
|
|
circle && CircularDependency.push(srcName);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
G = tmpG;
|
|
|
|
|
|
|
|
const noImportCode = noXtypeCode.replace(
|
|
|
|
/import {([\s\S]*?)} from "(.*?)";/g,
|
|
|
|
"",
|
|
|
|
);
|
|
|
|
|
|
|
|
let I = "";
|
|
|
|
Object.keys(G).forEach(key => {
|
|
|
|
let moduleKey = key;
|
|
|
|
if (moduleKey === path.basename(srcName).replace(/.js$/g, "")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let i = "";
|
|
|
|
Object.keys(G[moduleKey]).forEach(key => {
|
|
|
|
i += `${key}, `;
|
|
|
|
});
|
|
|
|
|
|
|
|
// 必须以 . 开头
|
|
|
|
const moduleInValid = /^[^@.]/.test(moduleKey);
|
|
|
|
if (moduleInValid) {
|
|
|
|
moduleKey = `./${moduleKey}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
I += `import {${i}} from '${moduleKey}'\n`;
|
|
|
|
});
|
|
|
|
const code = `${I}\n${noImportCode}`;
|
|
|
|
|
|
|
|
await saveAndFixCode(srcName, code);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
G["@/core"] = { shortcut: true };
|
|
|
|
|
|
|
|
const clzName = result[1];
|
|
|
|
|
|
|
|
if (!clzName) {
|
|
|
|
console.log(`${srcName} 不需要 es6 化`);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const superName = /inherit\(BI\.(.*?),/.exec(sourceCode)[1];
|
|
|
|
|
|
|
|
// const xtype = /BI.shortcut\(\"(.*?)\"/.exec(sourceCode)[1];
|
|
|
|
|
|
|
|
const collection = {
|
|
|
|
"static": {},
|
|
|
|
attr: {},
|
|
|
|
};
|
|
|
|
|
|
|
|
// eslint-disable-next-line no-unused-vars
|
|
|
|
const BI = {
|
|
|
|
[clzName]: clzName,
|
|
|
|
inherit(_, options) {
|
|
|
|
collection.methods = Object.keys(options)
|
|
|
|
.filter(key => typeof options[key] === "function")
|
|
|
|
.map(key => options[key]);
|
|
|
|
Object.keys(options)
|
|
|
|
.filter(key => typeof options[key] !== "function")
|
|
|
|
.forEach(key => {
|
|
|
|
collection.attr[key] = options[key];
|
|
|
|
});
|
|
|
|
|
|
|
|
return collection.static;
|
|
|
|
},
|
|
|
|
extend(targetClz, o) {
|
|
|
|
Object.assign(collection.static, o);
|
|
|
|
},
|
|
|
|
shortcut(xtype) {
|
|
|
|
collection.xtype = xtype;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
// eslint-disable-next-line no-eval
|
|
|
|
eval(sourceCode);
|
|
|
|
|
|
|
|
let M = "";
|
|
|
|
let E = "";
|
|
|
|
let I = "";
|
|
|
|
let A = "";
|
|
|
|
|
|
|
|
loader.load(srcName, superName);
|
|
|
|
|
|
|
|
Object.keys(collection.attr).forEach(key => {
|
|
|
|
const value = collection.attr[key];
|
|
|
|
if (typeof value === "function" || typeof value === "number") {
|
|
|
|
A += `\t${key} = ${value}\n`;
|
|
|
|
} else if (typeof value === "string") {
|
|
|
|
A += `\t${key} = "${value}"\n`;
|
|
|
|
} else if (typeof value === "object") {
|
|
|
|
if (objHaveFunction(value)) {
|
|
|
|
throw new Error("G");
|
|
|
|
} else {
|
|
|
|
A += `\t${key} = ${JSON.stringify(value)}\n`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// 静态方法
|
|
|
|
Object.keys(collection.static).forEach(key => {
|
|
|
|
// console.log(key, collection.static[key], typeof collection.static[key])
|
|
|
|
const value = collection.static[key];
|
|
|
|
if (typeof value === "function" || typeof value === "number") {
|
|
|
|
E += `\tstatic ${key} = ${value}\n`;
|
|
|
|
} else if (typeof value === "string") {
|
|
|
|
E += `\tstatic ${key} = "${value}"\n`;
|
|
|
|
} else if (typeof value === "object") {
|
|
|
|
if (objHaveFunction(value)) {
|
|
|
|
throw new Error("G");
|
|
|
|
} else {
|
|
|
|
E += `\tstatic ${key} = ${JSON.stringify(value)}\n`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// 对函数进行替换
|
|
|
|
collection.methods.forEach(el => {
|
|
|
|
let f = `${el.toString().replace(/^function/, el.name)}\n`;
|
|
|
|
|
|
|
|
// 换 BI.Button.superclass,就说能不能跑吧
|
|
|
|
for (let i = 0; i < 100; i++) {
|
|
|
|
f = f.replace(`BI.${clzName}.superclass`, "super");
|
|
|
|
}
|
|
|
|
// 换 super._defaultConfig
|
|
|
|
f = f.replace(
|
|
|
|
/super\._defaultConfig\.apply\(this,\sarguments\)/g,
|
|
|
|
"super._defaultConfig(...arguments)",
|
|
|
|
);
|
|
|
|
// 换 super.xxx.apply
|
|
|
|
f = f.replace(/super\.(.*?)\.apply\(this,\sarguments\)/g, a => {
|
|
|
|
const f = /super\.(.*?)\.apply\(this,\sarguments\)/.exec(a);
|
|
|
|
|
|
|
|
return `super.${f[1]}(...arguments)`;
|
|
|
|
});
|
|
|
|
|
|
|
|
// 尝试换掉所有的 BI.xxx. BI.xxx( BI.xxx[空白]
|
|
|
|
f = f.replace(/BI\.(.*?)(\.|\(|\s|,)/g, matchedSentence => {
|
|
|
|
const match = /BI\.(.*?)(\.|\(|\s|,)/.exec(matchedSentence);
|
|
|
|
const target = match[1];
|
|
|
|
const end = match[2];
|
|
|
|
// 尝试加载 target
|
|
|
|
const loadSuccess = loader.load(srcName, target);
|
|
|
|
if (loadSuccess) {
|
|
|
|
return target + end;
|
|
|
|
} else {
|
|
|
|
console.log(`BI 变量替换失败 BI.${target}`);
|
|
|
|
|
|
|
|
return matchedSentence;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// 尝试对 xtype 进行替换
|
|
|
|
f = f.replace(/"bi\.(.*?)"/g, matchedSentence => {
|
|
|
|
const loadSuccess = loader.load(srcName, matchedSentence);
|
|
|
|
if (loadSuccess) {
|
|
|
|
const clzName = depts[matchedSentence].clzName;
|
|
|
|
|
|
|
|
return `${clzName}.xtype`;
|
|
|
|
} else {
|
|
|
|
// console.log(`(没事) xtype 替换失败 ${matchedSentence} `);
|
|
|
|
|
|
|
|
return matchedSentence;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
M += `${f}\n`;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!collection.xtype) {
|
|
|
|
delete G["@/core"].shortcut;
|
|
|
|
}
|
|
|
|
|
|
|
|
Object.keys(G).forEach(key => {
|
|
|
|
let moduleKey = key;
|
|
|
|
if (moduleKey === path.basename(srcName).replace(/.js$/g, "")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let i = "";
|
|
|
|
Object.keys(G[moduleKey]).forEach(key => {
|
|
|
|
i += `${key}, `;
|
|
|
|
});
|
|
|
|
|
|
|
|
// 必须以 . 开头
|
|
|
|
const moduleInValid = /^[^@.]/.test(moduleKey);
|
|
|
|
if (moduleInValid) {
|
|
|
|
moduleKey = `./${moduleKey}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
I += `import {${i}} from '${moduleKey}'\n`;
|
|
|
|
});
|
|
|
|
|
|
|
|
const outputCode = `
|
|
|
|
${I}
|
|
|
|
|
|
|
|
${collection.xtype ? "@shortcut()" : ""}
|
|
|
|
export class ${clzName} extends ${superName} {
|
|
|
|
${collection.xtype ? `static xtype = "${collection.xtype}"` : ""}
|
|
|
|
|
|
|
|
${A}
|
|
|
|
|
|
|
|
${E}
|
|
|
|
|
|
|
|
${M}
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
await saveAndFixCode(srcName, outputCode);
|
|
|
|
|
|
|
|
return clzName;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function traverse(srcName) {
|
|
|
|
if (srcName.indexOf("__test__") >= 0) return;
|
|
|
|
|
|
|
|
if (srcName.endsWith(".js")) {
|
|
|
|
try {
|
|
|
|
return await handleFile(srcName);
|
|
|
|
} catch (error) {
|
|
|
|
console.log(`文件处理失败 ${srcName} \n`);
|
|
|
|
console.error(error);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const stat = fs.statSync(srcName);
|
|
|
|
const flag = stat.isDirectory();
|
|
|
|
if (flag) {
|
|
|
|
const files = fs.readdirSync(srcName);
|
|
|
|
// let indexContent = "";
|
|
|
|
for (let i = 0; i < files.length; i++) {
|
|
|
|
const file = files[i];
|
|
|
|
await traverse(path.resolve(srcName, file));
|
|
|
|
// const clzName = await traverse(path.resolve(srcName, file));
|
|
|
|
// const moduleName = path.basename(srcName).replace(/.js$/, "");
|
|
|
|
// if (clzName) {
|
|
|
|
// indexContent += `export { ${clzName} } from '${moduleName}'\n`;
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const srcName = process.argv[2];
|
|
|
|
|
|
|
|
initDepts().then(async () => {
|
|
|
|
await traverse(srcName);
|
|
|
|
|
|
|
|
// 对数据处理
|
|
|
|
ConflictImport.forEach(el => {
|
|
|
|
console.log(`导入冲突 ${el}`);
|
|
|
|
});
|
|
|
|
CircularDependency.forEach(el => {
|
|
|
|
console.log(`出现循环依赖(已经fixed) ${el}`);
|
|
|
|
});
|
|
|
|
});
|