2024-04-23 17:26:21 +08:00
|
|
|
|
(function (global, factory) {
|
2024-10-13 00:35:44 +08:00
|
|
|
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('fs'), require('path'), require('axios'), require('lodash'), require('prettier'), require('@vue/compiler-sfc'), require('magic-string'), require('glob'), require('svgo')) :
|
|
|
|
|
typeof define === 'function' && define.amd ? define(['exports', 'fs', 'path', 'axios', 'lodash', 'prettier', '@vue/compiler-sfc', 'magic-string', 'glob', 'svgo'], factory) :
|
|
|
|
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.index = {}, global.fs, global.path, global.axios, global.lodash, global.prettier, global.compilerSfc, global.magicString, global.glob, global.svgo));
|
|
|
|
|
})(this, (function (exports, fs, path, axios, lodash, prettier, compilerSfc, magicString, glob, svgo) { 'use strict';
|
2024-04-23 17:26:21 +08:00
|
|
|
|
|
|
|
|
|
const config = {
|
2024-04-26 21:28:51 +08:00
|
|
|
|
type: "admin",
|
2024-04-23 17:26:21 +08:00
|
|
|
|
reqUrl: "",
|
2024-04-26 21:28:51 +08:00
|
|
|
|
demo: false,
|
|
|
|
|
eps: {
|
2024-08-13 17:29:12 +08:00
|
|
|
|
enable: true,
|
2024-05-19 15:44:47 +08:00
|
|
|
|
api: "",
|
2024-04-26 21:28:51 +08:00
|
|
|
|
dist: "./build/cool",
|
|
|
|
|
mapping: [
|
|
|
|
|
{
|
|
|
|
|
// 自定义匹配
|
|
|
|
|
custom: ({ propertyName, type }) => {
|
|
|
|
|
// 如果没有,返回null或者不返回,则继续遍历其他匹配规则
|
|
|
|
|
return null;
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: "string",
|
|
|
|
|
test: ["varchar", "text", "simple-json"],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: "string[]",
|
|
|
|
|
test: ["simple-array"],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: "Date",
|
|
|
|
|
test: ["datetime", "date"],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: "number",
|
|
|
|
|
test: ["tinyint", "int", "decimal"],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
type: "BigInt",
|
|
|
|
|
test: ["bigint"],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
},
|
2024-04-23 17:26:21 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 根目录
|
|
|
|
|
function rootDir(path$1) {
|
|
|
|
|
switch (config.type) {
|
|
|
|
|
case "app":
|
|
|
|
|
return path.join(process.env.UNI_INPUT_DIR, path$1);
|
|
|
|
|
default:
|
|
|
|
|
return path.join(process.cwd(), path$1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 首字母大写
|
|
|
|
|
function firstUpperCase(value) {
|
|
|
|
|
return value.replace(/\b(\w)(\w*)/g, function ($0, $1, $2) {
|
|
|
|
|
return $1.toUpperCase() + $2;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 横杠转驼峰
|
|
|
|
|
function toCamel(str) {
|
|
|
|
|
return str.replace(/([^-])(?:-+([^-]))/g, function ($0, $1, $2) {
|
|
|
|
|
return $1 + $2.toUpperCase();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 创建目录
|
2024-04-26 21:28:51 +08:00
|
|
|
|
function createDir(path, recursive) {
|
|
|
|
|
try {
|
|
|
|
|
if (!fs.existsSync(path))
|
|
|
|
|
fs.mkdirSync(path, { recursive });
|
|
|
|
|
}
|
|
|
|
|
catch (err) { }
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
|
|
|
|
// 读取文件
|
|
|
|
|
function readFile(path, json) {
|
|
|
|
|
try {
|
|
|
|
|
const content = fs.readFileSync(path, "utf8");
|
|
|
|
|
return json ? JSON.parse(content) : content;
|
|
|
|
|
}
|
|
|
|
|
catch (err) { }
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
// 写入文件
|
|
|
|
|
function writeFile(path, data) {
|
|
|
|
|
try {
|
|
|
|
|
return fs.writeFileSync(path, data);
|
|
|
|
|
}
|
|
|
|
|
catch (err) { }
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
// 解析body
|
|
|
|
|
function parseJson(req) {
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
let d = "";
|
|
|
|
|
req.on("data", function (chunk) {
|
|
|
|
|
d += chunk;
|
|
|
|
|
});
|
|
|
|
|
req.on("end", function () {
|
|
|
|
|
try {
|
|
|
|
|
resolve(JSON.parse(d));
|
|
|
|
|
}
|
|
|
|
|
catch {
|
|
|
|
|
resolve({});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
function error(message) {
|
|
|
|
|
console.log("\x1B[31m%s\x1B[0m", message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let service = {};
|
|
|
|
|
let list = [];
|
|
|
|
|
let customList = [];
|
2024-05-19 15:44:47 +08:00
|
|
|
|
// 获取请求地址
|
|
|
|
|
function getEpsUrl() {
|
|
|
|
|
let url = config.eps.api;
|
|
|
|
|
if (!url) {
|
|
|
|
|
url = config.type;
|
|
|
|
|
}
|
|
|
|
|
switch (url) {
|
|
|
|
|
case "app":
|
|
|
|
|
url = "/app/base/comm/eps";
|
|
|
|
|
break;
|
|
|
|
|
case "admin":
|
|
|
|
|
url = "/admin/base/open/eps";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return url;
|
|
|
|
|
}
|
2024-04-26 21:28:51 +08:00
|
|
|
|
// 获取路径
|
|
|
|
|
function getEpsPath(filename) {
|
|
|
|
|
return path.join(config.type == "admin" ? config.eps.dist : rootDir(config.eps.dist), filename || "");
|
|
|
|
|
}
|
2024-04-23 17:26:21 +08:00
|
|
|
|
// 获取方法名
|
|
|
|
|
function getNames(v) {
|
|
|
|
|
return Object.keys(v).filter((e) => !["namespace", "permission"].includes(e));
|
|
|
|
|
}
|
|
|
|
|
// 获取数据
|
|
|
|
|
async function getData(data) {
|
|
|
|
|
// 自定义数据
|
2024-10-13 00:35:44 +08:00
|
|
|
|
if (!lodash.isEmpty(data)) {
|
2024-04-23 17:26:21 +08:00
|
|
|
|
customList = (data || []).map((e) => {
|
|
|
|
|
return {
|
|
|
|
|
...e,
|
|
|
|
|
isLocal: true,
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-10-13 00:12:45 +08:00
|
|
|
|
// 读取本地数据
|
|
|
|
|
list = readFile(getEpsPath("eps.json"), true) || [];
|
2024-04-23 17:26:21 +08:00
|
|
|
|
// 请求地址
|
2024-05-19 15:44:47 +08:00
|
|
|
|
const url = config.reqUrl + getEpsUrl();
|
2024-04-23 17:26:21 +08:00
|
|
|
|
// 请求数据
|
|
|
|
|
await axios
|
|
|
|
|
.get(url, {
|
|
|
|
|
timeout: 5000,
|
|
|
|
|
})
|
|
|
|
|
.then((res) => {
|
|
|
|
|
const { code, data, message } = res.data;
|
|
|
|
|
if (code === 1000) {
|
2024-10-13 00:35:44 +08:00
|
|
|
|
if (!lodash.isEmpty(data) && data) {
|
|
|
|
|
list = lodash.values(data).flat();
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2024-10-13 00:12:45 +08:00
|
|
|
|
error(`[cool-eps] ${message || "获取数据失败"}`);
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(() => {
|
2024-10-13 00:12:45 +08:00
|
|
|
|
error(`[cool-eps] 后端未启动 ➜ ${url}`);
|
2024-04-23 17:26:21 +08:00
|
|
|
|
});
|
|
|
|
|
// 合并自定义数据
|
2024-10-13 00:35:44 +08:00
|
|
|
|
if (lodash.isArray(customList)) {
|
2024-04-23 17:26:21 +08:00
|
|
|
|
customList.forEach((e) => {
|
|
|
|
|
const d = list.find((a) => e.prefix === a.prefix);
|
|
|
|
|
if (d) {
|
2024-10-13 00:35:44 +08:00
|
|
|
|
lodash.merge(d, e);
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
list.push(e);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 设置默认值
|
|
|
|
|
list.forEach((e) => {
|
|
|
|
|
if (!e.namespace) {
|
|
|
|
|
e.namespace = "";
|
|
|
|
|
}
|
|
|
|
|
if (!e.api) {
|
|
|
|
|
e.api = [];
|
|
|
|
|
}
|
2024-07-26 14:15:27 +08:00
|
|
|
|
if (!e.columns) {
|
|
|
|
|
e.columns = [];
|
|
|
|
|
}
|
2024-04-23 17:26:21 +08:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 创建 json 文件
|
|
|
|
|
function createJson() {
|
2024-05-17 14:03:57 +08:00
|
|
|
|
const arr = list.map((e) => {
|
2024-04-23 17:26:21 +08:00
|
|
|
|
return {
|
|
|
|
|
prefix: e.prefix,
|
|
|
|
|
name: e.name || "",
|
|
|
|
|
api: e.api.map((e) => {
|
|
|
|
|
return {
|
|
|
|
|
name: e.name,
|
|
|
|
|
method: e.method,
|
|
|
|
|
path: e.path,
|
|
|
|
|
};
|
|
|
|
|
}),
|
|
|
|
|
};
|
|
|
|
|
});
|
2024-05-17 14:03:57 +08:00
|
|
|
|
const content = JSON.stringify(arr);
|
|
|
|
|
const local_content = readFile(getEpsPath("eps.json"));
|
|
|
|
|
// 是否需要更新
|
|
|
|
|
const isUpdate = content != local_content;
|
|
|
|
|
if (isUpdate) {
|
|
|
|
|
fs.createWriteStream(getEpsPath("eps.json"), {
|
|
|
|
|
flags: "w",
|
|
|
|
|
}).write(content);
|
|
|
|
|
}
|
|
|
|
|
return isUpdate;
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
|
|
|
|
// 创建描述文件
|
|
|
|
|
async function createDescribe({ list, service }) {
|
|
|
|
|
// 获取类型
|
|
|
|
|
function getType({ propertyName, type }) {
|
2024-04-26 21:28:51 +08:00
|
|
|
|
for (const map of config.eps.mapping) {
|
|
|
|
|
if (map.custom) {
|
|
|
|
|
const resType = map.custom({ propertyName, type });
|
|
|
|
|
if (resType)
|
|
|
|
|
return resType;
|
|
|
|
|
}
|
2024-04-23 17:26:21 +08:00
|
|
|
|
if (map.test) {
|
|
|
|
|
if (map.test.includes(type))
|
|
|
|
|
return map.type;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return type;
|
|
|
|
|
}
|
2024-10-13 00:12:45 +08:00
|
|
|
|
// 格式化方法名
|
|
|
|
|
function formatName(name) {
|
|
|
|
|
return (name || "").replace(/[:,\s,\/,-]/g, "");
|
|
|
|
|
}
|
2024-04-23 17:26:21 +08:00
|
|
|
|
// 创建 Entity
|
|
|
|
|
function createEntity() {
|
2024-10-13 00:12:45 +08:00
|
|
|
|
const ignore = [];
|
|
|
|
|
let t0 = "";
|
2024-04-23 17:26:21 +08:00
|
|
|
|
for (const item of list) {
|
|
|
|
|
if (!item.name)
|
|
|
|
|
continue;
|
2024-10-13 00:12:45 +08:00
|
|
|
|
let t = `interface ${formatName(item.name)} {`;
|
2024-04-23 17:26:21 +08:00
|
|
|
|
for (const col of item.columns || []) {
|
2024-10-13 00:12:45 +08:00
|
|
|
|
t += `
|
|
|
|
|
/**
|
|
|
|
|
* ${col.comment}
|
|
|
|
|
*/
|
|
|
|
|
${col.propertyName}?: ${getType({
|
2024-04-23 17:26:21 +08:00
|
|
|
|
propertyName: col.propertyName,
|
|
|
|
|
type: col.type,
|
2024-10-13 00:12:45 +08:00
|
|
|
|
})}
|
|
|
|
|
`;
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
2024-10-13 00:12:45 +08:00
|
|
|
|
t += `
|
|
|
|
|
/**
|
|
|
|
|
* 任意键值
|
|
|
|
|
*/
|
|
|
|
|
[key: string]: any;
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
if (!ignore.includes(item.name)) {
|
|
|
|
|
ignore.push(item.name);
|
|
|
|
|
t0 += t;
|
2024-07-22 20:24:18 +08:00
|
|
|
|
}
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
2024-10-13 00:12:45 +08:00
|
|
|
|
return t0;
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
|
|
|
|
// 创建 Service
|
|
|
|
|
function createDts() {
|
2024-10-13 00:12:45 +08:00
|
|
|
|
let controller = "";
|
|
|
|
|
let chain = "";
|
2024-04-23 17:26:21 +08:00
|
|
|
|
// 处理数据
|
|
|
|
|
function deep(d, k) {
|
|
|
|
|
if (!k)
|
|
|
|
|
k = "";
|
|
|
|
|
for (const i in d) {
|
2024-10-13 00:12:45 +08:00
|
|
|
|
const name = k + toCamel(firstUpperCase(formatName(i)));
|
2024-04-23 17:26:21 +08:00
|
|
|
|
if (d[i].namespace) {
|
|
|
|
|
// 查找配置
|
|
|
|
|
const item = list.find((e) => (e.prefix || "") === `/${d[i].namespace}`);
|
|
|
|
|
if (item) {
|
2024-10-13 00:12:45 +08:00
|
|
|
|
let t = `interface ${name} {`;
|
2024-04-23 17:26:21 +08:00
|
|
|
|
// 插入方法
|
|
|
|
|
if (item.api) {
|
|
|
|
|
// 权限列表
|
|
|
|
|
const permission = [];
|
|
|
|
|
item.api.forEach((a) => {
|
|
|
|
|
// 方法名
|
2024-10-13 00:35:44 +08:00
|
|
|
|
const n = toCamel(formatName(a.name || lodash.last(a.path.split("/")) || ""));
|
2024-04-23 17:26:21 +08:00
|
|
|
|
if (n) {
|
|
|
|
|
// 参数类型
|
|
|
|
|
let q = [];
|
|
|
|
|
// 参数列表
|
|
|
|
|
const { parameters = [] } = a.dts || {};
|
|
|
|
|
parameters.forEach((p) => {
|
|
|
|
|
if (p.description) {
|
|
|
|
|
q.push(`\n/** ${p.description} */\n`);
|
|
|
|
|
}
|
|
|
|
|
if (p.name.includes(":")) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
const a = `${p.name}${p.required ? "" : "?"}`;
|
|
|
|
|
const b = `${p.schema.type || "string"}`;
|
|
|
|
|
q.push(`${a}: ${b},`);
|
|
|
|
|
});
|
2024-10-13 00:35:44 +08:00
|
|
|
|
if (lodash.isEmpty(q)) {
|
2024-04-23 17:26:21 +08:00
|
|
|
|
q = ["any"];
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
q.unshift("{");
|
|
|
|
|
q.push("}");
|
|
|
|
|
}
|
|
|
|
|
// 返回类型
|
|
|
|
|
let res = "";
|
|
|
|
|
// 实体名
|
|
|
|
|
const en = item.name || "any";
|
|
|
|
|
switch (a.path) {
|
|
|
|
|
case "/page":
|
|
|
|
|
res = `
|
|
|
|
|
{
|
|
|
|
|
pagination: { size: number; page: number; total: number; [key: string]: any };
|
|
|
|
|
list: ${en} [];
|
|
|
|
|
[key: string]: any;
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
break;
|
|
|
|
|
case "/list":
|
|
|
|
|
res = `${en} []`;
|
|
|
|
|
break;
|
|
|
|
|
case "/info":
|
|
|
|
|
res = en;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
res = "any";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
// 描述
|
2024-10-13 00:12:45 +08:00
|
|
|
|
t += `
|
|
|
|
|
/**
|
|
|
|
|
* ${a.summary || n}
|
|
|
|
|
*/
|
|
|
|
|
${n}(data${q.length == 1 ? "?" : ""}: ${q.join("")}): Promise<${res}>;
|
|
|
|
|
`;
|
2024-07-22 20:24:18 +08:00
|
|
|
|
if (!permission.includes(n)) {
|
|
|
|
|
permission.push(n);
|
|
|
|
|
}
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
// 权限标识
|
2024-10-13 00:12:45 +08:00
|
|
|
|
t += `
|
|
|
|
|
/**
|
|
|
|
|
* 权限标识
|
|
|
|
|
*/
|
|
|
|
|
permission: { ${permission.map((e) => `${e}: string;`).join("\n")} };
|
|
|
|
|
`;
|
2024-04-23 17:26:21 +08:00
|
|
|
|
// 权限状态
|
2024-10-13 00:12:45 +08:00
|
|
|
|
t += `
|
|
|
|
|
/**
|
|
|
|
|
* 权限状态
|
|
|
|
|
*/
|
|
|
|
|
_permission: { ${permission.map((e) => `${e}: boolean;`).join("\n")} };
|
|
|
|
|
`;
|
|
|
|
|
t += `
|
|
|
|
|
request: Service['request']
|
|
|
|
|
`;
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
2024-10-13 00:12:45 +08:00
|
|
|
|
t += "}\n\n";
|
|
|
|
|
controller += t;
|
|
|
|
|
chain += `${formatName(i)}: ${name};`;
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2024-10-13 00:12:45 +08:00
|
|
|
|
chain += `${formatName(i)}: {`;
|
2024-04-23 17:26:21 +08:00
|
|
|
|
deep(d[i], name);
|
2024-10-13 00:12:45 +08:00
|
|
|
|
chain += "},";
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-10-13 00:12:45 +08:00
|
|
|
|
// 遍历
|
2024-04-23 17:26:21 +08:00
|
|
|
|
deep(service);
|
2024-10-13 00:12:45 +08:00
|
|
|
|
return `
|
|
|
|
|
type json = any;
|
|
|
|
|
|
|
|
|
|
${controller}
|
|
|
|
|
|
|
|
|
|
type Service = {
|
|
|
|
|
/**
|
|
|
|
|
* 基础请求
|
|
|
|
|
*/
|
|
|
|
|
request(options?: {
|
|
|
|
|
url: string;
|
|
|
|
|
method?: "POST" | "GET" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
|
|
|
|
|
data?: any;
|
|
|
|
|
params?: any;
|
|
|
|
|
headers?: {
|
|
|
|
|
authorization?: string;
|
|
|
|
|
[key: string]: any;
|
|
|
|
|
},
|
|
|
|
|
timeout?: number;
|
|
|
|
|
proxy?: boolean;
|
|
|
|
|
[key: string]: any;
|
|
|
|
|
}): Promise<any>;
|
|
|
|
|
|
|
|
|
|
${chain}
|
|
|
|
|
}
|
|
|
|
|
`;
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
|
|
|
|
// 文件内容
|
|
|
|
|
const text = `
|
|
|
|
|
declare namespace Eps {
|
|
|
|
|
${createEntity()}
|
|
|
|
|
${createDts()}
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
// 文本内容
|
|
|
|
|
const content = await prettier.format(text, {
|
|
|
|
|
parser: "typescript",
|
|
|
|
|
useTabs: true,
|
|
|
|
|
tabWidth: 4,
|
|
|
|
|
endOfLine: "lf",
|
|
|
|
|
semi: true,
|
|
|
|
|
singleQuote: false,
|
|
|
|
|
printWidth: 100,
|
|
|
|
|
trailingComma: "none",
|
|
|
|
|
});
|
2024-05-17 14:03:57 +08:00
|
|
|
|
const local_content = readFile(getEpsPath("eps.d.ts"));
|
|
|
|
|
// 是否需要更新
|
|
|
|
|
if (content != local_content) {
|
|
|
|
|
// 创建 eps 描述文件
|
|
|
|
|
fs.createWriteStream(getEpsPath("eps.d.ts"), {
|
|
|
|
|
flags: "w",
|
|
|
|
|
}).write(content);
|
|
|
|
|
}
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
|
|
|
|
// 创建 service
|
|
|
|
|
function createService() {
|
2024-05-19 15:44:47 +08:00
|
|
|
|
// 路径第一层作为 id 标识
|
|
|
|
|
const id = getEpsUrl().split("/")[1];
|
2024-04-23 17:26:21 +08:00
|
|
|
|
list.forEach((e) => {
|
2024-05-19 15:44:47 +08:00
|
|
|
|
// 请求地址
|
|
|
|
|
const path = e.prefix[0] == "/" ? e.prefix.substring(1, e.prefix.length) : e.prefix;
|
2024-04-23 17:26:21 +08:00
|
|
|
|
// 分隔路径
|
2024-05-19 15:44:47 +08:00
|
|
|
|
const arr = path.replace(id, "").split("/").filter(Boolean).map(toCamel);
|
2024-04-23 17:26:21 +08:00
|
|
|
|
// 遍历
|
|
|
|
|
function deep(d, i) {
|
|
|
|
|
const k = arr[i];
|
|
|
|
|
if (k) {
|
|
|
|
|
// 是否最后一个
|
|
|
|
|
if (arr[i + 1]) {
|
|
|
|
|
if (!d[k]) {
|
|
|
|
|
d[k] = {};
|
|
|
|
|
}
|
|
|
|
|
deep(d[k], i + 1);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// 不存在则创建
|
|
|
|
|
if (!d[k]) {
|
|
|
|
|
d[k] = {
|
2024-05-19 15:44:47 +08:00
|
|
|
|
namespace: path,
|
2024-04-23 17:26:21 +08:00
|
|
|
|
permission: {},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
// 创建方法
|
|
|
|
|
e.api.forEach((a) => {
|
|
|
|
|
// 方法名
|
|
|
|
|
const n = a.path.replace("/", "");
|
|
|
|
|
if (n && !/[-:]/g.test(n)) {
|
|
|
|
|
d[k][n] = a;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
// 创建权限
|
2024-05-19 15:44:47 +08:00
|
|
|
|
getNames(d[k]).forEach((i) => {
|
|
|
|
|
d[k].permission[i] = `${d[k].namespace.replace(`${id}/`, "")}/${i}`.replace(/\//g, ":");
|
2024-04-23 17:26:21 +08:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
deep(service, 0);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 创建 eps
|
|
|
|
|
async function createEps(query) {
|
2024-08-13 17:29:12 +08:00
|
|
|
|
if (config.eps.enable) {
|
|
|
|
|
// 获取数据
|
|
|
|
|
await getData(query?.list || []);
|
|
|
|
|
// 创建 service
|
|
|
|
|
createService();
|
|
|
|
|
// 创建目录
|
|
|
|
|
createDir(getEpsPath(), true);
|
|
|
|
|
// 创建 json 文件
|
|
|
|
|
const isUpdate = createJson();
|
|
|
|
|
// 创建描述文件
|
|
|
|
|
createDescribe({ service, list });
|
|
|
|
|
return {
|
|
|
|
|
service,
|
|
|
|
|
list,
|
|
|
|
|
isUpdate,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return {
|
|
|
|
|
service: {},
|
|
|
|
|
list: [],
|
|
|
|
|
};
|
|
|
|
|
}
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createTag(code, id) {
|
|
|
|
|
if (/\.vue$/.test(id)) {
|
|
|
|
|
let s;
|
|
|
|
|
const str = () => s || (s = new magicString(code));
|
|
|
|
|
const { descriptor } = compilerSfc.parse(code);
|
|
|
|
|
if (!descriptor.script && descriptor.scriptSetup) {
|
|
|
|
|
const res = compilerSfc.compileScript(descriptor, { id });
|
|
|
|
|
const { name, lang } = res.attrs;
|
|
|
|
|
str().appendLeft(0, `<script lang="${lang}">
|
|
|
|
|
import { defineComponent } from 'vue'
|
|
|
|
|
export default defineComponent({
|
|
|
|
|
name: "${name}"
|
|
|
|
|
})
|
|
|
|
|
<\/script>`);
|
|
|
|
|
return {
|
|
|
|
|
map: str().generateMap(),
|
|
|
|
|
code: str().toString()
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 创建文件
|
|
|
|
|
async function createMenu(options) {
|
|
|
|
|
// 格式化内容
|
|
|
|
|
const content = await prettier.format(options.code, {
|
|
|
|
|
parser: "vue",
|
|
|
|
|
useTabs: true,
|
|
|
|
|
tabWidth: 4,
|
|
|
|
|
endOfLine: "lf",
|
|
|
|
|
semi: true,
|
|
|
|
|
jsxBracketSameLine: true,
|
|
|
|
|
singleQuote: false,
|
|
|
|
|
printWidth: 100,
|
|
|
|
|
trailingComma: "none",
|
|
|
|
|
});
|
|
|
|
|
// 目录路径
|
|
|
|
|
const dir = (options.viewPath || "").split("/");
|
|
|
|
|
// 文件名
|
|
|
|
|
const fname = dir.pop();
|
2024-04-26 21:28:51 +08:00
|
|
|
|
// 源码路径
|
|
|
|
|
const srcPath = `./src/${dir.join("/")}`;
|
2024-04-23 17:26:21 +08:00
|
|
|
|
// 创建目录
|
2024-04-26 21:28:51 +08:00
|
|
|
|
createDir(srcPath, true);
|
2024-04-23 17:26:21 +08:00
|
|
|
|
// 创建文件
|
2024-04-26 21:28:51 +08:00
|
|
|
|
fs.createWriteStream(path.join(srcPath, fname || "demo"), {
|
2024-04-23 17:26:21 +08:00
|
|
|
|
flags: "w",
|
|
|
|
|
}).write(content);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function base() {
|
|
|
|
|
return {
|
|
|
|
|
name: "vite-cool-base",
|
|
|
|
|
enforce: "pre",
|
|
|
|
|
configureServer(server) {
|
|
|
|
|
server.middlewares.use(async (req, res, next) => {
|
|
|
|
|
function done(data) {
|
|
|
|
|
res.writeHead(200, { "Content-Type": "text/html;charset=UTF-8" });
|
|
|
|
|
res.end(JSON.stringify(data));
|
|
|
|
|
}
|
|
|
|
|
if (req.originalUrl?.includes("__cool")) {
|
|
|
|
|
const body = await parseJson(req);
|
|
|
|
|
switch (req.url) {
|
|
|
|
|
// 快速创建菜单
|
|
|
|
|
case "/__cool_createMenu":
|
|
|
|
|
await createMenu(body);
|
|
|
|
|
break;
|
|
|
|
|
// 创建描述文件
|
|
|
|
|
case "/__cool_eps":
|
|
|
|
|
await createEps(body);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return done({
|
|
|
|
|
code: 1001,
|
|
|
|
|
message: "未知请求",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
done({
|
|
|
|
|
code: 1000,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
next();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
transform(code, id) {
|
|
|
|
|
if (config.type == "admin") {
|
|
|
|
|
return createTag(code, id);
|
|
|
|
|
}
|
|
|
|
|
return code;
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function demo(enable) {
|
|
|
|
|
const virtualModuleIds = ["virtual:demo"];
|
|
|
|
|
return {
|
|
|
|
|
name: "vite-cool-demo",
|
|
|
|
|
enforce: "pre",
|
|
|
|
|
resolveId(id) {
|
|
|
|
|
if (virtualModuleIds.includes(id)) {
|
|
|
|
|
return "\0" + id;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
async load(id) {
|
|
|
|
|
if (id === "\0virtual:demo") {
|
|
|
|
|
const demo = {};
|
|
|
|
|
if (enable) {
|
|
|
|
|
const files = await glob.glob(rootDir("./src/modules/demo/views/crud/components") + "/**", {
|
|
|
|
|
stat: true,
|
|
|
|
|
withFileTypes: true,
|
|
|
|
|
});
|
|
|
|
|
for (const file of files) {
|
|
|
|
|
if (file.isFile()) {
|
|
|
|
|
const p = path.join(file.path, file.name);
|
|
|
|
|
demo[p
|
|
|
|
|
.replace(/\\/g, "/")
|
|
|
|
|
.split("src/modules/demo/views/crud/components/")[1]] = fs.readFileSync(p, "utf-8");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return `
|
|
|
|
|
export const demo = ${JSON.stringify(demo)};
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function createCtx() {
|
2024-07-27 15:38:50 +08:00
|
|
|
|
let ctx = {
|
|
|
|
|
serviceLang: "Node",
|
|
|
|
|
};
|
2024-04-23 17:26:21 +08:00
|
|
|
|
if (config.type == "app") {
|
|
|
|
|
const manifest = readFile(rootDir("manifest.json"), true);
|
|
|
|
|
// 文件路径
|
|
|
|
|
const ctxPath = rootDir("pages.json");
|
|
|
|
|
// 页面配置
|
|
|
|
|
ctx = readFile(ctxPath, true);
|
|
|
|
|
// 原数据,做更新比较用
|
2024-10-13 00:35:44 +08:00
|
|
|
|
const ctxData = lodash.cloneDeep(ctx);
|
2024-04-23 17:26:21 +08:00
|
|
|
|
// 删除临时页面
|
|
|
|
|
ctx.pages = ctx.pages?.filter((e) => !e.isTemp);
|
|
|
|
|
ctx.subPackages = ctx.subPackages?.filter((e) => !e.isTemp);
|
|
|
|
|
// 加载 uni_modules 配置文件
|
|
|
|
|
const files = await glob.glob(rootDir("uni_modules") + "/**/pages_init.json", {
|
|
|
|
|
stat: true,
|
|
|
|
|
withFileTypes: true,
|
|
|
|
|
});
|
|
|
|
|
for (const file of files) {
|
|
|
|
|
if (file.isFile()) {
|
|
|
|
|
const { pages = [], subPackages = [] } = readFile(path.join(file.path, file.name), true);
|
|
|
|
|
// 合并到 pages 中
|
|
|
|
|
[...pages, ...subPackages].forEach((e) => {
|
|
|
|
|
e.isTemp = true;
|
|
|
|
|
const isSub = !!e.root;
|
|
|
|
|
const d = isSub
|
|
|
|
|
? ctx.subPackages?.find((a) => a.root == e.root)
|
|
|
|
|
: ctx.pages?.find((a) => a.path == e.path);
|
|
|
|
|
if (d) {
|
2024-10-13 00:35:44 +08:00
|
|
|
|
lodash.assign(d, e);
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (isSub) {
|
|
|
|
|
ctx.subPackages?.unshift(e);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ctx.pages?.unshift(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-17 14:03:57 +08:00
|
|
|
|
// 排序后检测,避免加载顺序问题
|
|
|
|
|
function order(d) {
|
|
|
|
|
return {
|
2024-10-13 00:35:44 +08:00
|
|
|
|
pages: lodash.orderBy(d.pages, "path"),
|
|
|
|
|
subPackages: lodash.orderBy(d.subPackages, "root"),
|
2024-05-17 14:03:57 +08:00
|
|
|
|
};
|
|
|
|
|
}
|
2024-04-23 17:26:21 +08:00
|
|
|
|
// 是否需要更新 pages.json
|
2024-10-13 00:35:44 +08:00
|
|
|
|
if (!lodash.isEqual(order(ctxData), order(ctx))) {
|
2024-04-23 17:26:21 +08:00
|
|
|
|
console.log("[cool-ctx] pages updated");
|
|
|
|
|
writeFile(ctxPath, JSON.stringify(ctx, null, 4));
|
|
|
|
|
}
|
|
|
|
|
// appid
|
|
|
|
|
ctx.appid = manifest.appid;
|
|
|
|
|
}
|
|
|
|
|
if (config.type == "admin") {
|
|
|
|
|
const list = fs.readdirSync(rootDir("./src/modules"));
|
|
|
|
|
ctx.modules = list.filter((e) => !e.includes("."));
|
2024-07-27 15:38:50 +08:00
|
|
|
|
await axios
|
|
|
|
|
.get(config.reqUrl + "/admin/base/comm/program", {
|
|
|
|
|
timeout: 5000,
|
|
|
|
|
})
|
|
|
|
|
.then((res) => {
|
|
|
|
|
const { code, data, message } = res.data;
|
|
|
|
|
if (code === 1000) {
|
|
|
|
|
ctx.serviceLang = data || "Node";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
error(`[cool-ctx] ${message}`);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch((err) => {
|
|
|
|
|
// console.error(['[cool-ctx] ', err.message])
|
|
|
|
|
});
|
2024-04-23 17:26:21 +08:00
|
|
|
|
}
|
|
|
|
|
return ctx;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-29 18:28:09 +08:00
|
|
|
|
let svgIcons = [];
|
|
|
|
|
function findSvg(dir) {
|
|
|
|
|
const arr = [];
|
|
|
|
|
const dirs = fs.readdirSync(dir, {
|
|
|
|
|
withFileTypes: true,
|
|
|
|
|
});
|
|
|
|
|
for (const d of dirs) {
|
|
|
|
|
if (d.isDirectory()) {
|
|
|
|
|
arr.push(...findSvg(dir + d.name + "/"));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (path.extname(d.name) == ".svg") {
|
|
|
|
|
svgIcons.push(path.basename(d.name, ".svg"));
|
|
|
|
|
const svg = fs.readFileSync(dir + d.name)
|
|
|
|
|
.toString()
|
|
|
|
|
.replace(/(\r)|(\n)/g, "")
|
|
|
|
|
.replace(/<svg([^>+].*?)>/, (_, $2) => {
|
|
|
|
|
let width = 0;
|
|
|
|
|
let height = 0;
|
|
|
|
|
let content = $2.replace(/(width|height)="([^>+].*?)"/g, (_, s2, s3) => {
|
|
|
|
|
if (s2 === "width") {
|
|
|
|
|
width = s3;
|
|
|
|
|
}
|
|
|
|
|
else if (s2 === "height") {
|
|
|
|
|
height = s3;
|
|
|
|
|
}
|
|
|
|
|
return "";
|
|
|
|
|
});
|
|
|
|
|
if (!/(viewBox="[^>+].*?")/g.test($2)) {
|
|
|
|
|
content += `viewBox="0 0 ${width} ${height}"`;
|
|
|
|
|
}
|
|
|
|
|
return `<symbol id="icon-${d.name.replace(".svg", "")}" ${content}>`;
|
|
|
|
|
})
|
|
|
|
|
.replace("</svg>", "</symbol>");
|
|
|
|
|
arr.push(svg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return arr;
|
|
|
|
|
}
|
|
|
|
|
function compilerSvg() {
|
|
|
|
|
svgIcons = [];
|
|
|
|
|
return findSvg(rootDir("./src/"))
|
|
|
|
|
.map((e) => {
|
|
|
|
|
return svgo.optimize(e)?.data || e;
|
|
|
|
|
})
|
|
|
|
|
.join("");
|
|
|
|
|
}
|
|
|
|
|
async function createSvg() {
|
|
|
|
|
const html = compilerSvg();
|
|
|
|
|
const code = `
|
|
|
|
|
if (typeof window !== 'undefined') {
|
|
|
|
|
function loadSvg() {
|
|
|
|
|
const svgDom = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
|
|
|
svgDom.style.position = 'absolute';
|
|
|
|
|
svgDom.style.width = '0';
|
|
|
|
|
svgDom.style.height = '0';
|
|
|
|
|
svgDom.setAttribute('xmlns','http://www.w3.org/2000/svg');
|
|
|
|
|
svgDom.setAttribute('xmlns:link','http://www.w3.org/1999/xlink');
|
|
|
|
|
svgDom.innerHTML = '${html}';
|
|
|
|
|
document.body.insertBefore(svgDom, document.body.firstChild);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loadSvg();
|
|
|
|
|
}
|
|
|
|
|
`;
|
|
|
|
|
return { code, svgIcons };
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-23 17:26:21 +08:00
|
|
|
|
async function virtual() {
|
2024-07-29 18:28:09 +08:00
|
|
|
|
const virtualModuleIds = [
|
|
|
|
|
"virtual:eps",
|
|
|
|
|
"virtual:ctx",
|
|
|
|
|
"virtual:svg-register",
|
|
|
|
|
"virtual:svg-icons",
|
|
|
|
|
];
|
2024-04-23 17:26:21 +08:00
|
|
|
|
return {
|
|
|
|
|
name: "vite-cool-virtual",
|
|
|
|
|
enforce: "pre",
|
2024-04-27 18:42:29 +08:00
|
|
|
|
configureServer(server) {
|
|
|
|
|
server.middlewares.use(async (req, res, next) => {
|
|
|
|
|
// 页面刷新时触发
|
|
|
|
|
if (req.url == "/@vite/client") {
|
|
|
|
|
// 重新加载虚拟模块
|
|
|
|
|
virtualModuleIds.forEach((vm) => {
|
|
|
|
|
const mod = server.moduleGraph.getModuleById(`\0${vm}`);
|
|
|
|
|
if (mod) {
|
|
|
|
|
server.moduleGraph.invalidateModule(mod);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
next();
|
|
|
|
|
});
|
|
|
|
|
},
|
2024-04-23 17:26:21 +08:00
|
|
|
|
handleHotUpdate({ file, server }) {
|
2024-04-27 18:42:29 +08:00
|
|
|
|
// 文件修改时触发
|
|
|
|
|
if (!["pages.json", "dist", "build/cool", "eps.json", "eps.d.ts"].some((e) => file.includes(e))) {
|
2024-04-23 17:26:21 +08:00
|
|
|
|
createCtx();
|
|
|
|
|
createEps().then((data) => {
|
2024-05-17 14:03:57 +08:00
|
|
|
|
if (data.isUpdate) {
|
|
|
|
|
// 通知客户端刷新
|
|
|
|
|
(server.hot || server.ws).send({
|
|
|
|
|
type: "custom",
|
|
|
|
|
event: "eps-update",
|
|
|
|
|
data,
|
|
|
|
|
});
|
|
|
|
|
}
|
2024-04-23 17:26:21 +08:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
resolveId(id) {
|
|
|
|
|
if (virtualModuleIds.includes(id)) {
|
|
|
|
|
return "\0" + id;
|
|
|
|
|
}
|
|
|
|
|
},
|
2024-04-27 18:42:29 +08:00
|
|
|
|
async load(id) {
|
2024-04-23 17:26:21 +08:00
|
|
|
|
if (id === "\0virtual:eps") {
|
2024-04-27 18:42:29 +08:00
|
|
|
|
const eps = await createEps();
|
2024-04-23 17:26:21 +08:00
|
|
|
|
return `
|
|
|
|
|
export const eps = ${JSON.stringify(eps)}
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
if (id === "\0virtual:ctx") {
|
2024-04-27 18:42:29 +08:00
|
|
|
|
const ctx = await createCtx();
|
2024-04-23 17:26:21 +08:00
|
|
|
|
return `
|
|
|
|
|
export const ctx = ${JSON.stringify(ctx)}
|
|
|
|
|
`;
|
|
|
|
|
}
|
2024-07-29 18:28:09 +08:00
|
|
|
|
if (id == "\0virtual:svg-register") {
|
|
|
|
|
const { code } = await createSvg();
|
|
|
|
|
return code;
|
|
|
|
|
}
|
|
|
|
|
if (id == "\0virtual:svg-icons") {
|
|
|
|
|
const { svgIcons } = await createSvg();
|
|
|
|
|
return `
|
|
|
|
|
export const svgIcons = ${JSON.stringify(svgIcons)}
|
|
|
|
|
`;
|
|
|
|
|
}
|
2024-04-23 17:26:21 +08:00
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function cool(options) {
|
2024-04-26 21:28:51 +08:00
|
|
|
|
// 应用类型,admin | app
|
2024-04-23 17:26:21 +08:00
|
|
|
|
config.type = options.type;
|
2024-04-26 21:28:51 +08:00
|
|
|
|
// 请求地址
|
2024-04-23 17:26:21 +08:00
|
|
|
|
config.reqUrl = options.proxy["/dev/"].target;
|
2024-04-26 21:28:51 +08:00
|
|
|
|
// Eps
|
|
|
|
|
if (options.eps) {
|
2024-08-13 17:29:12 +08:00
|
|
|
|
const { dist, mapping, api, enable = true } = options.eps;
|
|
|
|
|
// 是否开启
|
|
|
|
|
config.eps.enable = enable;
|
2024-05-19 15:44:47 +08:00
|
|
|
|
// 类型
|
|
|
|
|
if (api) {
|
|
|
|
|
config.eps.api = api;
|
|
|
|
|
}
|
|
|
|
|
// 输出目录
|
2024-04-26 21:28:51 +08:00
|
|
|
|
if (dist) {
|
|
|
|
|
config.eps.dist = dist;
|
|
|
|
|
}
|
2024-05-19 15:44:47 +08:00
|
|
|
|
// 匹配规则
|
2024-04-26 21:28:51 +08:00
|
|
|
|
if (mapping) {
|
2024-10-13 00:35:44 +08:00
|
|
|
|
lodash.merge(config.eps.mapping, mapping);
|
2024-04-26 21:28:51 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-23 17:26:21 +08:00
|
|
|
|
return [base(), virtual(), demo(options.demo)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exports.cool = cool;
|
|
|
|
|
|
|
|
|
|
}));
|