mirror of
https://github.com/cool-team-official/cool-admin-vue.git
synced 2024-11-01 06:02:38 +08:00
新增 vite-plugin 开源包
This commit is contained in:
parent
5a51d07c6d
commit
abf04749e8
1210
build/cool/dist/eps.d.ts
vendored
1210
build/cool/dist/eps.d.ts
vendored
File diff suppressed because it is too large
Load Diff
1
build/cool/dist/eps.json
vendored
1
build/cool/dist/eps.json
vendored
File diff suppressed because one or more lines are too long
@ -1,39 +0,0 @@
|
||||
import { join } from "path";
|
||||
|
||||
// 打包路径
|
||||
export const DistPath = join(__dirname, "../dist");
|
||||
|
||||
// 实体描述
|
||||
export const Entity = {
|
||||
mapping: [
|
||||
{
|
||||
// 自定义匹配
|
||||
custom: ({ propertyName, type }) => {
|
||||
// status 原本是tinyint,如果是1的话,== true 是可以的,但是不能 === true,请谨慎使用
|
||||
if (propertyName === "status" && type == "tinyint") return "boolean";
|
||||
// 如果没有,返回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"]
|
||||
}
|
||||
]
|
||||
};
|
@ -1,14 +0,0 @@
|
||||
import { base } from "./base";
|
||||
import { virtual } from "./virtual";
|
||||
import { demo } from "./demo";
|
||||
|
||||
export function cool(test?: boolean) {
|
||||
return [
|
||||
// 基础
|
||||
base(),
|
||||
// 虚拟模块
|
||||
virtual(),
|
||||
// demo 官方示例,代码片段
|
||||
demo(test)
|
||||
];
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
import fs from "fs";
|
||||
|
||||
export function createModule() {
|
||||
let dirs: string[] = [];
|
||||
|
||||
try {
|
||||
dirs = fs.readdirSync("./src/modules");
|
||||
dirs = dirs.filter((e) => !e.includes("."));
|
||||
} catch (err) {}
|
||||
|
||||
return { dirs };
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
import type { Plugin } from "vite";
|
||||
import { createEps } from "./eps";
|
||||
import { createModule } from "./module";
|
||||
|
||||
export function virtual(): Plugin {
|
||||
const virtualModuleIds = ["virtual:eps", "virtual:module"];
|
||||
|
||||
// 首次启动加载 Eps
|
||||
createEps();
|
||||
|
||||
return {
|
||||
name: "vite-cool-virtual",
|
||||
enforce: "pre",
|
||||
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();
|
||||
});
|
||||
},
|
||||
async handleHotUpdate({ file, server }) {
|
||||
// 代码保存时触发
|
||||
if (!["dist"].some((e) => file.includes(e))) {
|
||||
createEps().then((data) => {
|
||||
// 通知客户端刷新
|
||||
server.ws.send({
|
||||
type: "custom",
|
||||
event: "eps-update",
|
||||
data
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
resolveId(id) {
|
||||
if (virtualModuleIds.includes(id)) {
|
||||
return "\0" + id;
|
||||
}
|
||||
},
|
||||
async load(id) {
|
||||
if (id === "\0virtual:eps") {
|
||||
const { service } = await createEps();
|
||||
|
||||
return `
|
||||
export const eps = ${JSON.stringify({ service })}
|
||||
`;
|
||||
}
|
||||
|
||||
if (id === "\0virtual:module") {
|
||||
const { dirs } = createModule();
|
||||
|
||||
return `
|
||||
export const dirs = ${JSON.stringify(dirs)}
|
||||
`;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@ -37,6 +37,7 @@
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cool-vue/vite-plugin": "^7.0.1",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/lodash-es": "^4.17.8",
|
||||
"@types/mockjs": "^1.0.7",
|
||||
@ -52,8 +53,6 @@
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-vue": "^9.17.0",
|
||||
"glob": "^10.3.10",
|
||||
"magic-string": "^0.30.3",
|
||||
"prettier": "^3.1.0",
|
||||
"rollup-plugin-visualizer": "^5.9.2",
|
||||
"sass": "^1.66.1",
|
||||
|
12
packages/vite-plugin/.eslintrc.js
Normal file
12
packages/vite-plugin/.eslintrc.js
Normal file
@ -0,0 +1,12 @@
|
||||
export default {
|
||||
parser: "@typescript-eslint/parser",
|
||||
extends: ["plugin:@typescript-eslint/recommended"],
|
||||
parserOptions: {
|
||||
project: "./tsconfig.json",
|
||||
tsconfigRootDir: __dirname,
|
||||
ecmaVersion: 2020,
|
||||
sourceType: "module",
|
||||
createDefaultProgram: true,
|
||||
},
|
||||
rules: {},
|
||||
};
|
8
packages/vite-plugin/.prettierrc
Normal file
8
packages/vite-plugin/.prettierrc
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"tabWidth": 4,
|
||||
"useTabs": true,
|
||||
"semi": true,
|
||||
"jsxBracketSameLine": true,
|
||||
"singleQuote": false,
|
||||
"printWidth": 100
|
||||
}
|
789
packages/vite-plugin/dist/index.js
vendored
Normal file
789
packages/vite-plugin/dist/index.js
vendored
Normal file
@ -0,0 +1,789 @@
|
||||
(function (global, factory) {
|
||||
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')) :
|
||||
typeof define === 'function' && define.amd ? define(['exports', 'fs', 'path', 'axios', 'lodash', 'prettier', '@vue/compiler-sfc', 'magic-string', 'glob'], 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));
|
||||
})(this, (function (exports, fs, path, axios, lodash, prettier, compilerSfc, magicString, glob) { 'use strict';
|
||||
|
||||
const config = {
|
||||
type: "",
|
||||
reqUrl: "",
|
||||
};
|
||||
|
||||
// 根目录
|
||||
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();
|
||||
});
|
||||
}
|
||||
// 创建目录
|
||||
function createDir(path) {
|
||||
if (!fs.existsSync(path))
|
||||
fs.mkdirSync(path);
|
||||
}
|
||||
// 读取文件
|
||||
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 mkdirs(path$1) {
|
||||
const arr = path$1.split("/");
|
||||
let p = "";
|
||||
arr.forEach((e) => {
|
||||
const t = path.join(p, e);
|
||||
try {
|
||||
fs.statSync(t);
|
||||
}
|
||||
catch (err) {
|
||||
try {
|
||||
fs.mkdirSync(t);
|
||||
}
|
||||
catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
p = t;
|
||||
});
|
||||
return p;
|
||||
}
|
||||
function error(message) {
|
||||
console.log("\x1B[31m%s\x1B[0m", message);
|
||||
}
|
||||
|
||||
// 打包目录
|
||||
const DistDir = path.join(__dirname, "../");
|
||||
// 实体描述
|
||||
const Entity = {
|
||||
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"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// eps 数据文件路径
|
||||
const epsJsonPath = path.join(DistDir, "eps.json");
|
||||
// eps 描述文件路径
|
||||
const epsDtsPath = path.join(DistDir, "eps.d.ts");
|
||||
let service = {};
|
||||
let list = [];
|
||||
let customList = [];
|
||||
// 获取方法名
|
||||
function getNames(v) {
|
||||
return Object.keys(v).filter((e) => !["namespace", "permission"].includes(e));
|
||||
}
|
||||
// 获取数据
|
||||
async function getData(data) {
|
||||
// 自定义数据
|
||||
if (!lodash.isEmpty(data)) {
|
||||
customList = (data || []).map((e) => {
|
||||
return {
|
||||
...e,
|
||||
isLocal: true,
|
||||
};
|
||||
});
|
||||
}
|
||||
// 本地文件
|
||||
try {
|
||||
list = readFile(epsJsonPath, true) || [];
|
||||
}
|
||||
catch (err) {
|
||||
error(`[cool-eps] ${epsJsonPath} 文件异常, ${err.message}`);
|
||||
}
|
||||
// 请求地址
|
||||
let url = config.reqUrl;
|
||||
switch (config.type) {
|
||||
case "app":
|
||||
url += "/app/base/comm/eps";
|
||||
break;
|
||||
case "admin":
|
||||
url += "/admin/base/open/eps";
|
||||
break;
|
||||
}
|
||||
// 请求数据
|
||||
await axios
|
||||
.get(url, {
|
||||
timeout: 5000,
|
||||
})
|
||||
.then((res) => {
|
||||
const { code, data, message } = res.data;
|
||||
if (code === 1000) {
|
||||
if (!lodash.isEmpty(data) && data) {
|
||||
lodash.merge(list, Object.values(data).flat());
|
||||
}
|
||||
}
|
||||
else {
|
||||
error(`[cool-eps] ${message}`);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
error(`[cool-eps] 后端未启动 ➜ ${url}`);
|
||||
});
|
||||
// 合并自定义数据
|
||||
if (lodash.isArray(customList)) {
|
||||
customList.forEach((e) => {
|
||||
const d = list.find((a) => e.prefix === a.prefix);
|
||||
if (d) {
|
||||
lodash.merge(d, e);
|
||||
}
|
||||
else {
|
||||
list.push(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 设置默认值
|
||||
list.forEach((e) => {
|
||||
if (!e.namespace) {
|
||||
e.namespace = "";
|
||||
}
|
||||
if (!e.api) {
|
||||
e.api = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
// 创建 json 文件
|
||||
function createJson() {
|
||||
const d = list.map((e) => {
|
||||
return {
|
||||
prefix: e.prefix,
|
||||
name: e.name || "",
|
||||
api: e.api.map((e) => {
|
||||
return {
|
||||
name: e.name,
|
||||
method: e.method,
|
||||
path: e.path,
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
fs.createWriteStream(epsJsonPath, {
|
||||
flags: "w",
|
||||
}).write(JSON.stringify(d));
|
||||
}
|
||||
// 创建描述文件
|
||||
async function createDescribe({ list, service }) {
|
||||
// 获取类型
|
||||
function getType({ propertyName, type }) {
|
||||
for (const map of Entity.mapping) {
|
||||
// if (map.custom) {
|
||||
// const resType = map.custom({ propertyName, type });
|
||||
// if (resType) return resType;
|
||||
// }
|
||||
if (map.test) {
|
||||
if (map.test.includes(type))
|
||||
return map.type;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
// 创建 Entity
|
||||
function createEntity() {
|
||||
const t0 = [];
|
||||
for (const item of list) {
|
||||
if (!item.name)
|
||||
continue;
|
||||
const t = [`interface ${item.name} {`];
|
||||
for (const col of item.columns || []) {
|
||||
// 描述
|
||||
t.push("\n");
|
||||
t.push("/**\n");
|
||||
t.push(` * ${col.comment}\n`);
|
||||
t.push(" */\n");
|
||||
t.push(`${col.propertyName}?: ${getType({
|
||||
propertyName: col.propertyName,
|
||||
type: col.type,
|
||||
})};`);
|
||||
}
|
||||
t.push("\n");
|
||||
t.push("/**\n");
|
||||
t.push(` * 任意键值\n`);
|
||||
t.push(" */\n");
|
||||
t.push(`[key: string]: any;`);
|
||||
t.push("}");
|
||||
t0.push(t);
|
||||
}
|
||||
return t0.map((e) => e.join("")).join("\n\n");
|
||||
}
|
||||
// 创建 Service
|
||||
function createDts() {
|
||||
const t0 = [];
|
||||
const t1 = [
|
||||
`
|
||||
type json = any;
|
||||
|
||||
type Service = {
|
||||
request(options?: {
|
||||
url: string;
|
||||
method?: "POST" | "GET" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
|
||||
data?: any;
|
||||
params?: any;
|
||||
headers?: {
|
||||
[key: string]: any;
|
||||
},
|
||||
timeout?: number;
|
||||
proxy?: boolean;
|
||||
[key: string]: any;
|
||||
}): Promise<any>;
|
||||
`,
|
||||
];
|
||||
// 处理数据
|
||||
function deep(d, k) {
|
||||
if (!k)
|
||||
k = "";
|
||||
for (const i in d) {
|
||||
const name = k + toCamel(firstUpperCase(i.replace(/[:]/g, "")));
|
||||
if (d[i].namespace) {
|
||||
// 查找配置
|
||||
const item = list.find((e) => (e.prefix || "") === `/${d[i].namespace}`);
|
||||
if (item) {
|
||||
const t = [`interface ${name} {`];
|
||||
t1.push(`${i}: ${name};`);
|
||||
// 插入方法
|
||||
if (item.api) {
|
||||
// 权限列表
|
||||
const permission = [];
|
||||
item.api.forEach((a) => {
|
||||
// 方法名
|
||||
const n = toCamel(a.name || lodash.last(a.path.split("/")) || "").replace(/[:\/-]/g, "");
|
||||
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},`);
|
||||
});
|
||||
if (lodash.isEmpty(q)) {
|
||||
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;
|
||||
}
|
||||
// 描述
|
||||
t.push("\n");
|
||||
t.push("/**\n");
|
||||
t.push(` * ${a.summary || n}\n`);
|
||||
t.push(" */\n");
|
||||
t.push(`${n}(data${q.length == 1 ? "?" : ""}: ${q.join("")}): Promise<${res}>;`);
|
||||
permission.push(n);
|
||||
}
|
||||
});
|
||||
// 权限标识
|
||||
t.push("\n");
|
||||
t.push("/**\n");
|
||||
t.push(" * 权限标识\n");
|
||||
t.push(" */\n");
|
||||
t.push(`permission: { ${permission
|
||||
.map((e) => `${e}: string;`)
|
||||
.join("\n")} };`);
|
||||
// 权限状态
|
||||
t.push("\n");
|
||||
t.push("/**\n");
|
||||
t.push(" * 权限状态\n");
|
||||
t.push(" */\n");
|
||||
t.push(`_permission: { ${permission
|
||||
.map((e) => `${e}: boolean;`)
|
||||
.join("\n")} };`);
|
||||
// 请求
|
||||
t.push("\n");
|
||||
t.push("/**\n");
|
||||
t.push(" * 请求\n");
|
||||
t.push(" */\n");
|
||||
t.push(`request: Service['request']`);
|
||||
}
|
||||
t.push("}");
|
||||
t0.push(t);
|
||||
}
|
||||
}
|
||||
else {
|
||||
t1.push(`${i}: {`);
|
||||
deep(d[i], name);
|
||||
t1.push(`},`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 深度
|
||||
deep(service);
|
||||
// 结束
|
||||
t1.push("}");
|
||||
// 追加
|
||||
t0.push(t1);
|
||||
return t0.map((e) => e.join("")).join("\n\n");
|
||||
}
|
||||
// 文件内容
|
||||
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",
|
||||
});
|
||||
// 创建 eps 描述文件
|
||||
fs.createWriteStream(epsDtsPath, {
|
||||
flags: "w",
|
||||
}).write(content);
|
||||
}
|
||||
// 创建 service
|
||||
function createService() {
|
||||
list.forEach((e) => {
|
||||
// 分隔路径
|
||||
const arr = e.prefix
|
||||
.replace(/\//, "")
|
||||
.replace(config.type, "")
|
||||
.split("/")
|
||||
.filter(Boolean)
|
||||
.map(toCamel);
|
||||
// 遍历
|
||||
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] = {
|
||||
namespace: e.prefix.substring(1, e.prefix.length),
|
||||
permission: {},
|
||||
};
|
||||
}
|
||||
// 创建方法
|
||||
e.api.forEach((a) => {
|
||||
// 方法名
|
||||
const n = a.path.replace("/", "");
|
||||
if (n && !/[-:]/g.test(n)) {
|
||||
d[k][n] = a;
|
||||
}
|
||||
});
|
||||
// 创建权限
|
||||
getNames(d[k]).forEach((e) => {
|
||||
d[k].permission[e] =
|
||||
`${d[k].namespace.replace(`${config.type}/`, "")}/${e}`.replace(/\//g, ":");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
deep(service, 0);
|
||||
});
|
||||
}
|
||||
// 创建 eps
|
||||
async function createEps(query) {
|
||||
// 获取数据
|
||||
await getData(query?.list || []);
|
||||
// 创建 service
|
||||
createService();
|
||||
// 创建临时目录
|
||||
createDir(DistDir);
|
||||
// 创建 json 文件
|
||||
createJson();
|
||||
// 创建描述文件
|
||||
createDescribe({ service, list });
|
||||
return {
|
||||
service,
|
||||
list,
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
function findFiles(dir) {
|
||||
const res = [];
|
||||
const dirs = fs.readdirSync(dir, {
|
||||
withFileTypes: true,
|
||||
});
|
||||
for (const d of dirs) {
|
||||
if (d.isDirectory()) {
|
||||
res.push(...findFiles(dir + d.name + "/"));
|
||||
}
|
||||
else {
|
||||
if (path.extname(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>");
|
||||
res.push(svg);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
function createSvg(html) {
|
||||
const res = findFiles(rootDir("./src/"));
|
||||
return html.replace("<body>", `<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
|
||||
${res.join("")}
|
||||
</svg>`);
|
||||
}
|
||||
|
||||
// 创建文件
|
||||
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();
|
||||
// 创建目录
|
||||
const path$1 = mkdirs(rootDir(`./src/${dir.join("/")}`));
|
||||
// 创建文件
|
||||
fs.createWriteStream(path.join(path$1, fname || "demo"), {
|
||||
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;
|
||||
},
|
||||
transformIndexHtml(html) {
|
||||
if (config.type == "admin") {
|
||||
return createSvg(html);
|
||||
}
|
||||
return html;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
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() {
|
||||
let ctx = {};
|
||||
if (config.type == "app") {
|
||||
const manifest = readFile(rootDir("manifest.json"), true);
|
||||
// 文件路径
|
||||
const ctxPath = rootDir("pages.json");
|
||||
// 页面配置
|
||||
ctx = readFile(ctxPath, true);
|
||||
// 原数据,做更新比较用
|
||||
const ctxData = lodash.cloneDeep(ctx);
|
||||
// 删除临时页面
|
||||
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) {
|
||||
lodash.assign(d, e);
|
||||
}
|
||||
else {
|
||||
if (isSub) {
|
||||
ctx.subPackages?.unshift(e);
|
||||
}
|
||||
else {
|
||||
ctx.pages?.unshift(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
// 是否需要更新 pages.json
|
||||
if (!lodash.isEqual(ctxData, ctx)) {
|
||||
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("."));
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
async function virtual() {
|
||||
const virtualModuleIds = ["virtual:eps", "virtual:ctx"];
|
||||
const eps = await createEps();
|
||||
const ctx = await createCtx();
|
||||
return {
|
||||
name: "vite-cool-virtual",
|
||||
enforce: "pre",
|
||||
handleHotUpdate({ file, server }) {
|
||||
if (!["pages.json", "dist"].some((e) => file.includes(e))) {
|
||||
createCtx();
|
||||
createEps().then((data) => {
|
||||
// 通知客户端刷新
|
||||
server.ws.send({
|
||||
type: "custom",
|
||||
event: "eps-update",
|
||||
data,
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
resolveId(id) {
|
||||
if (virtualModuleIds.includes(id)) {
|
||||
return "\0" + id;
|
||||
}
|
||||
},
|
||||
load(id) {
|
||||
if (id === "\0virtual:eps") {
|
||||
return `
|
||||
export const eps = ${JSON.stringify(eps)}
|
||||
`;
|
||||
}
|
||||
if (id === "\0virtual:ctx") {
|
||||
return `
|
||||
export const ctx = ${JSON.stringify(ctx)}
|
||||
`;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function cool(options) {
|
||||
config.type = options.type;
|
||||
config.reqUrl = options.proxy["/dev/"].target;
|
||||
return [base(), virtual(), demo(options.demo)];
|
||||
}
|
||||
|
||||
exports.cool = cool;
|
||||
|
||||
}));
|
2
packages/vite-plugin/dist/types/src/base.d.ts
vendored
Normal file
2
packages/vite-plugin/dist/types/src/base.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import type { Plugin } from "vite";
|
||||
export declare function base(): Plugin;
|
5
packages/vite-plugin/dist/types/src/config.d.ts
vendored
Normal file
5
packages/vite-plugin/dist/types/src/config.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
import type { Type } from "./types";
|
||||
export declare const config: {
|
||||
type: Type;
|
||||
reqUrl: string;
|
||||
};
|
2
packages/vite-plugin/dist/types/src/ctx/index.d.ts
vendored
Normal file
2
packages/vite-plugin/dist/types/src/ctx/index.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import type { Ctx } from "../types";
|
||||
export declare function createCtx(): Promise<Ctx.Data>;
|
2
packages/vite-plugin/dist/types/src/demo.d.ts
vendored
Normal file
2
packages/vite-plugin/dist/types/src/demo.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import type { Plugin } from "vite";
|
||||
export declare function demo(enable?: boolean): Plugin;
|
7
packages/vite-plugin/dist/types/src/eps/config.d.ts
vendored
Normal file
7
packages/vite-plugin/dist/types/src/eps/config.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
export declare const DistDir: string;
|
||||
export declare const Entity: {
|
||||
mapping: {
|
||||
type: string;
|
||||
test: string[];
|
||||
}[];
|
||||
};
|
7
packages/vite-plugin/dist/types/src/eps/index.d.ts
vendored
Normal file
7
packages/vite-plugin/dist/types/src/eps/index.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
import type { Eps } from "../types";
|
||||
export declare function createEps(query?: {
|
||||
list: any[];
|
||||
}): Promise<{
|
||||
service: {};
|
||||
list: Eps.Entity[];
|
||||
}>;
|
6
packages/vite-plugin/dist/types/src/index.d.ts
vendored
Normal file
6
packages/vite-plugin/dist/types/src/index.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
import type { Type } from "./types";
|
||||
export declare function cool(options: {
|
||||
type: Type;
|
||||
proxy: any;
|
||||
demo?: boolean;
|
||||
}): (import("vite").Plugin<any> | Promise<import("vite").Plugin<any>>)[];
|
4
packages/vite-plugin/dist/types/src/menu/index.d.ts
vendored
Normal file
4
packages/vite-plugin/dist/types/src/menu/index.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
export declare function createMenu(options: {
|
||||
viewPath: string;
|
||||
code: string;
|
||||
}): Promise<void>;
|
1
packages/vite-plugin/dist/types/src/svg/index.d.ts
vendored
Normal file
1
packages/vite-plugin/dist/types/src/svg/index.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export declare function createSvg(html: string): string;
|
4
packages/vite-plugin/dist/types/src/tag/index.d.ts
vendored
Normal file
4
packages/vite-plugin/dist/types/src/tag/index.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
export declare function createTag(code: string, id: string): {
|
||||
map: any;
|
||||
code: any;
|
||||
} | null;
|
10
packages/vite-plugin/dist/types/src/utils/index.d.ts
vendored
Normal file
10
packages/vite-plugin/dist/types/src/utils/index.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
export declare function rootDir(path: string): string;
|
||||
export declare function firstUpperCase(value: string): string;
|
||||
export declare function toCamel(str: string): string;
|
||||
export declare function createDir(path: string): void;
|
||||
export declare function readFile(path: string, json?: boolean): any;
|
||||
export declare function writeFile(path: string, data: any): void | "";
|
||||
export declare function parseJson(req: any): Promise<any>;
|
||||
export declare function mkdirs(path: string): string;
|
||||
export declare function error(message: string): void;
|
||||
export declare function success(message: string): void;
|
2
packages/vite-plugin/dist/types/src/virtual.d.ts
vendored
Normal file
2
packages/vite-plugin/dist/types/src/virtual.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
import type { Plugin } from "vite";
|
||||
export declare function virtual(): Promise<Plugin>;
|
1
packages/vite-plugin/eps.d.ts
vendored
Normal file
1
packages/vite-plugin/eps.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="./src/dist/eps" />
|
33
packages/vite-plugin/package.json
Normal file
33
packages/vite-plugin/package.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "@cool-vue/vite-plugin",
|
||||
"version": "7.0.1",
|
||||
"description": "cool-admin/cool-uni builder",
|
||||
"main": "/dist/index.js",
|
||||
"scripts": {
|
||||
"build": "rollup --c --bundleConfigAsCjs"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "cool",
|
||||
"license": "ISC",
|
||||
"types": "./dist/types/src/index.d.ts",
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-typescript": "^11.1.6",
|
||||
"@types/lodash": "^4.17.0",
|
||||
"@types/node": "^20.12.7",
|
||||
"@typescript-eslint/eslint-plugin": "^7.7.1",
|
||||
"@typescript-eslint/parser": "^7.7.1",
|
||||
"eslint": "^9.1.1",
|
||||
"rollup": "^4.16.2",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^5.2.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vue/compiler-sfc": "^3.4.24",
|
||||
"axios": "^1.6.8",
|
||||
"glob": "^10.3.12",
|
||||
"lodash": "^4.17.21",
|
||||
"magic-string": "^0.30.10",
|
||||
"prettier": "^3.2.5"
|
||||
}
|
||||
}
|
1767
packages/vite-plugin/pnpm-lock.yaml
Normal file
1767
packages/vite-plugin/pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load Diff
20
packages/vite-plugin/rollup.config.js
Normal file
20
packages/vite-plugin/rollup.config.js
Normal file
@ -0,0 +1,20 @@
|
||||
import typescript from "@rollup/plugin-typescript";
|
||||
|
||||
import { defineConfig } from "rollup";
|
||||
|
||||
export default defineConfig({
|
||||
input: ["src/index.ts"],
|
||||
|
||||
output: {
|
||||
name: "index",
|
||||
format: "umd",
|
||||
file: "dist/index.js",
|
||||
},
|
||||
|
||||
plugins: [
|
||||
typescript({
|
||||
module: "esnext",
|
||||
exclude: ["./node_modules/**"],
|
||||
}),
|
||||
],
|
||||
});
|
@ -1,9 +1,10 @@
|
||||
import type { Plugin } from "vite";
|
||||
import { createSvg } from "./svg";
|
||||
import { createTag } from "./tag";
|
||||
import { createEps } from "./eps";
|
||||
import { createMenu } from "./menu";
|
||||
import { parseJson } from "./utils";
|
||||
import { createTag } from "./tag";
|
||||
import { createSvg } from "./svg";
|
||||
import { createMenu } from "./menu";
|
||||
import { config } from "./config";
|
||||
|
||||
export function base(): Plugin {
|
||||
return {
|
||||
@ -16,7 +17,7 @@ export function base(): Plugin {
|
||||
res.end(JSON.stringify(data));
|
||||
}
|
||||
|
||||
if (req.url?.includes("__cool")) {
|
||||
if (req.originalUrl?.includes("__cool")) {
|
||||
const body = await parseJson(req);
|
||||
|
||||
switch (req.url) {
|
||||
@ -33,12 +34,12 @@ export function base(): Plugin {
|
||||
default:
|
||||
return done({
|
||||
code: 1001,
|
||||
message: "未知请求"
|
||||
message: "未知请求",
|
||||
});
|
||||
}
|
||||
|
||||
done({
|
||||
code: 1000
|
||||
code: 1000,
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
@ -46,10 +47,18 @@ export function base(): Plugin {
|
||||
});
|
||||
},
|
||||
transform(code, id) {
|
||||
return createTag(code, id);
|
||||
if (config.type == "admin") {
|
||||
return createTag(code, id);
|
||||
}
|
||||
|
||||
return code;
|
||||
},
|
||||
transformIndexHtml(html) {
|
||||
return createSvg(html);
|
||||
}
|
||||
if (config.type == "admin") {
|
||||
return createSvg(html);
|
||||
}
|
||||
|
||||
return html;
|
||||
},
|
||||
};
|
||||
}
|
6
packages/vite-plugin/src/config.ts
Normal file
6
packages/vite-plugin/src/config.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import type { Type } from "./types";
|
||||
|
||||
export const config = {
|
||||
type: "" as Type,
|
||||
reqUrl: "",
|
||||
};
|
80
packages/vite-plugin/src/ctx/index.ts
Normal file
80
packages/vite-plugin/src/ctx/index.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import { join } from "path";
|
||||
import { readFile, rootDir, writeFile } from "../utils";
|
||||
import { glob } from "glob";
|
||||
import { assign, cloneDeep, isEqual } from "lodash";
|
||||
import type { Ctx } from "../types";
|
||||
import { config } from "../config";
|
||||
import fs from "fs";
|
||||
|
||||
export async function createCtx() {
|
||||
let ctx: Ctx.Data = {};
|
||||
|
||||
if (config.type == "app") {
|
||||
const manifest = readFile(rootDir("manifest.json"), true);
|
||||
|
||||
// 文件路径
|
||||
const ctxPath = rootDir("pages.json");
|
||||
|
||||
// 页面配置
|
||||
ctx = readFile(ctxPath, true);
|
||||
|
||||
// 原数据,做更新比较用
|
||||
const ctxData = cloneDeep(ctx);
|
||||
|
||||
// 删除临时页面
|
||||
ctx.pages = ctx.pages?.filter((e) => !e.isTemp);
|
||||
ctx.subPackages = ctx.subPackages?.filter((e) => !e.isTemp);
|
||||
|
||||
// 加载 uni_modules 配置文件
|
||||
const files = await glob(rootDir("uni_modules") + "/**/pages_init.json", {
|
||||
stat: true,
|
||||
withFileTypes: true,
|
||||
});
|
||||
|
||||
for (const file of files) {
|
||||
if (file.isFile()) {
|
||||
const { pages = [], subPackages = [] }: Ctx.Data = readFile(
|
||||
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) {
|
||||
assign(d, e);
|
||||
} else {
|
||||
if (isSub) {
|
||||
ctx.subPackages?.unshift(e);
|
||||
} else {
|
||||
ctx.pages?.unshift(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 是否需要更新 pages.json
|
||||
if (!isEqual(ctxData, ctx)) {
|
||||
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("."));
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
@ -2,6 +2,7 @@ import type { Plugin } from "vite";
|
||||
import { glob } from "glob";
|
||||
import path from "path";
|
||||
import { readFileSync } from "fs";
|
||||
import { rootDir } from "./utils";
|
||||
|
||||
export function demo(enable?: boolean): Plugin {
|
||||
const virtualModuleIds = ["virtual:demo"];
|
||||
@ -16,13 +17,16 @@ export function demo(enable?: boolean): Plugin {
|
||||
},
|
||||
async load(id) {
|
||||
if (id === "\0virtual:demo") {
|
||||
const demo = {};
|
||||
const demo: any = {};
|
||||
|
||||
if (enable) {
|
||||
const files = await glob("./src/modules/demo/views/crud/components/**", {
|
||||
stat: true,
|
||||
withFileTypes: true
|
||||
});
|
||||
const files = await glob(
|
||||
rootDir("./src/modules/demo/views/crud/components") + "/**",
|
||||
{
|
||||
stat: true,
|
||||
withFileTypes: true,
|
||||
},
|
||||
);
|
||||
|
||||
for (const file of files) {
|
||||
if (file.isFile()) {
|
||||
@ -41,6 +45,6 @@ export function demo(enable?: boolean): Plugin {
|
||||
export const demo = ${JSON.stringify(demo)};
|
||||
`;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
1380
packages/vite-plugin/src/dist/eps.d.ts
vendored
Normal file
1380
packages/vite-plugin/src/dist/eps.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
packages/vite-plugin/src/dist/eps.json
vendored
Normal file
1
packages/vite-plugin/src/dist/eps.json
vendored
Normal file
File diff suppressed because one or more lines are too long
37
packages/vite-plugin/src/eps/config.ts
Normal file
37
packages/vite-plugin/src/eps/config.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { join } from "path";
|
||||
|
||||
// 打包目录
|
||||
export const DistDir = join(__dirname, "../");
|
||||
|
||||
// 实体描述
|
||||
export const Entity = {
|
||||
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"],
|
||||
},
|
||||
],
|
||||
};
|
@ -1,66 +1,83 @@
|
||||
import { createDir, error, firstUpperCase, readFile, toCamel } from "../utils";
|
||||
import { join } from "path";
|
||||
import { Entity, DistPath } from "./config";
|
||||
import { Entity, DistDir } from "./config";
|
||||
import axios from "axios";
|
||||
import { isArray, isEmpty, last, merge, unionBy } from "lodash-es";
|
||||
import { isArray, isEmpty, last, merge } from "lodash";
|
||||
import { createWriteStream } from "fs";
|
||||
import prettier from "prettier";
|
||||
import { proxy } from "../../../src/config/proxy";
|
||||
import { config } from "../config";
|
||||
import type { Eps } from "../types";
|
||||
|
||||
// eps 数据文件路径
|
||||
const epsJsonPath = join(DistDir, "eps.json");
|
||||
|
||||
// eps 描述文件路径
|
||||
const epsDtsPath = join(DistDir, "eps.d.ts");
|
||||
|
||||
let service = {};
|
||||
let list: Eps.Entity[] = [];
|
||||
let customList: Eps.Entity[] = [];
|
||||
|
||||
// 获取方法名
|
||||
function getNames(v: any) {
|
||||
return Object.keys(v).filter((e) => !["namespace", "permission"].includes(e));
|
||||
}
|
||||
|
||||
// 数据
|
||||
const service = {};
|
||||
let list: Eps.Entity[] = [];
|
||||
let localList: Eps.Entity[] = [];
|
||||
|
||||
// 获取数据
|
||||
async function getData(temps?: Eps.Entity[]) {
|
||||
// 记录本地数据
|
||||
if (!isEmpty(temps)) {
|
||||
localList = (temps || []).map((e) => {
|
||||
async function getData(data?: Eps.Entity[]) {
|
||||
// 自定义数据
|
||||
if (!isEmpty(data)) {
|
||||
customList = (data || []).map((e) => {
|
||||
return {
|
||||
...e,
|
||||
isLocal: true
|
||||
isLocal: true,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// 本地文件
|
||||
try {
|
||||
list = JSON.parse(readFile(join(DistPath, "eps.json")) || "[]");
|
||||
list = readFile(epsJsonPath, true) || [];
|
||||
} catch (err: any) {
|
||||
error(`[cool-eps] ${join(DistPath, "eps.json")} 文件异常, ${err.message}`);
|
||||
error(`[cool-eps] ${epsJsonPath} 文件异常, ${err.message}`);
|
||||
}
|
||||
|
||||
// 远程地址
|
||||
const url = proxy["/dev/"].target + "/admin/base/open/eps";
|
||||
// 请求地址
|
||||
let url = config.reqUrl;
|
||||
|
||||
switch (config.type) {
|
||||
case "app":
|
||||
url += "/app/base/comm/eps";
|
||||
break;
|
||||
|
||||
case "admin":
|
||||
url += "/admin/base/open/eps";
|
||||
break;
|
||||
}
|
||||
|
||||
// 请求数据
|
||||
await axios
|
||||
.get(url, {
|
||||
timeout: 5000
|
||||
timeout: 5000,
|
||||
})
|
||||
.then((res) => {
|
||||
const { code, data } = res.data;
|
||||
const { code, data, message } = res.data;
|
||||
|
||||
if (code === 1000) {
|
||||
if (!isEmpty(data) && data) {
|
||||
merge(list, Object.values(data).flat() as Eps.Entity[]);
|
||||
}
|
||||
} else {
|
||||
error(`[cool-eps] ${message}`);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
error(`[cool-eps] 后端未启动 ➜ ${url}`);
|
||||
});
|
||||
|
||||
// 合并本地数据
|
||||
if (isArray(localList)) {
|
||||
localList.forEach((e) => {
|
||||
// 合并自定义数据
|
||||
if (isArray(customList)) {
|
||||
customList.forEach((e) => {
|
||||
const d = list.find((a) => e.prefix === a.prefix);
|
||||
|
||||
if (d) {
|
||||
@ -71,29 +88,36 @@ async function getData(temps?: Eps.Entity[]) {
|
||||
});
|
||||
}
|
||||
|
||||
list = unionBy(list, "prefix");
|
||||
// 设置默认值
|
||||
list.forEach((e) => {
|
||||
if (!e.namespace) {
|
||||
e.namespace = "";
|
||||
}
|
||||
|
||||
if (!e.api) {
|
||||
e.api = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 创建 json 文件
|
||||
function createJson() {
|
||||
const d = list
|
||||
.filter((e) => !e.isLocal)
|
||||
.map((e) => {
|
||||
return {
|
||||
prefix: e.prefix,
|
||||
name: e.name || "",
|
||||
api: e.api.map((e) => {
|
||||
return {
|
||||
name: e.name,
|
||||
method: e.method,
|
||||
path: e.path
|
||||
};
|
||||
})
|
||||
};
|
||||
});
|
||||
const d = list.map((e) => {
|
||||
return {
|
||||
prefix: e.prefix,
|
||||
name: e.name || "",
|
||||
api: e.api.map((e) => {
|
||||
return {
|
||||
name: e.name,
|
||||
method: e.method,
|
||||
path: e.path,
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
createWriteStream(join(DistPath, "eps.json"), {
|
||||
flags: "w"
|
||||
createWriteStream(epsJsonPath, {
|
||||
flags: "w",
|
||||
}).write(JSON.stringify(d));
|
||||
}
|
||||
|
||||
@ -102,10 +126,10 @@ async function createDescribe({ list, service }: { list: Eps.Entity[]; service:
|
||||
// 获取类型
|
||||
function getType({ propertyName, type }: any) {
|
||||
for (const map of Entity.mapping) {
|
||||
if (map.custom) {
|
||||
const resType = map.custom({ propertyName, type });
|
||||
if (resType) return resType;
|
||||
}
|
||||
// if (map.custom) {
|
||||
// const resType = map.custom({ propertyName, type });
|
||||
// if (resType) return resType;
|
||||
// }
|
||||
if (map.test) {
|
||||
if (map.test.includes(type)) return map.type;
|
||||
}
|
||||
@ -120,7 +144,6 @@ async function createDescribe({ list, service }: { list: Eps.Entity[]; service:
|
||||
for (const item of list) {
|
||||
if (!item.name) continue;
|
||||
const t = [`interface ${item.name} {`];
|
||||
|
||||
for (const col of item.columns || []) {
|
||||
// 描述
|
||||
t.push("\n");
|
||||
@ -130,8 +153,8 @@ async function createDescribe({ list, service }: { list: Eps.Entity[]; service:
|
||||
t.push(
|
||||
`${col.propertyName}?: ${getType({
|
||||
propertyName: col.propertyName,
|
||||
type: col.type
|
||||
})};`
|
||||
type: col.type,
|
||||
})};`,
|
||||
);
|
||||
}
|
||||
t.push("\n");
|
||||
@ -167,7 +190,7 @@ async function createDescribe({ list, service }: { list: Eps.Entity[]; service:
|
||||
proxy?: boolean;
|
||||
[key: string]: any;
|
||||
}): Promise<any>;
|
||||
`
|
||||
`,
|
||||
];
|
||||
|
||||
// 处理数据
|
||||
@ -193,9 +216,9 @@ async function createDescribe({ list, service }: { list: Eps.Entity[]; service:
|
||||
|
||||
item.api.forEach((a) => {
|
||||
// 方法名
|
||||
const n = (a.name || last(a.path.split("/")) || "").replace(
|
||||
/[:\/]/g,
|
||||
""
|
||||
const n = toCamel(a.name || last(a.path.split("/")) || "").replace(
|
||||
/[:\/-]/g,
|
||||
"",
|
||||
);
|
||||
|
||||
if (n) {
|
||||
@ -217,7 +240,7 @@ async function createDescribe({ list, service }: { list: Eps.Entity[]; service:
|
||||
const a = `${p.name}${p.required ? "" : "?"}`;
|
||||
const b = `${p.schema.type || "string"}`;
|
||||
|
||||
q.push(`"${a}": ${b},`);
|
||||
q.push(`${a}: ${b},`);
|
||||
});
|
||||
|
||||
if (isEmpty(q)) {
|
||||
@ -264,14 +287,12 @@ async function createDescribe({ list, service }: { list: Eps.Entity[]; service:
|
||||
t.push(" */\n");
|
||||
|
||||
t.push(
|
||||
`"${n}"(data${q.length == 1 ? "?" : ""}: ${q.join(
|
||||
""
|
||||
)}): Promise<${res}>;`
|
||||
`${n}(data${q.length == 1 ? "?" : ""}: ${q.join(
|
||||
"",
|
||||
)}): Promise<${res}>;`,
|
||||
);
|
||||
|
||||
if (!permission.includes(n)) {
|
||||
permission.push(n);
|
||||
}
|
||||
permission.push(n);
|
||||
}
|
||||
});
|
||||
|
||||
@ -282,8 +303,8 @@ async function createDescribe({ list, service }: { list: Eps.Entity[]; service:
|
||||
t.push(" */\n");
|
||||
t.push(
|
||||
`permission: { ${permission
|
||||
.map((e) => `"${e}": string;`)
|
||||
.join("\n")} };`
|
||||
.map((e) => `${e}: string;`)
|
||||
.join("\n")} };`,
|
||||
);
|
||||
|
||||
// 权限状态
|
||||
@ -293,8 +314,8 @@ async function createDescribe({ list, service }: { list: Eps.Entity[]; service:
|
||||
t.push(" */\n");
|
||||
t.push(
|
||||
`_permission: { ${permission
|
||||
.map((e) => `"${e}": boolean;`)
|
||||
.join("\n")} };`
|
||||
.map((e) => `${e}: boolean;`)
|
||||
.join("\n")} };`,
|
||||
);
|
||||
|
||||
// 请求
|
||||
@ -345,12 +366,12 @@ async function createDescribe({ list, service }: { list: Eps.Entity[]; service:
|
||||
semi: true,
|
||||
singleQuote: false,
|
||||
printWidth: 100,
|
||||
trailingComma: "none"
|
||||
trailingComma: "none",
|
||||
});
|
||||
|
||||
// 创建 eps 描述文件
|
||||
createWriteStream(join(DistPath, "eps.d.ts"), {
|
||||
flags: "w"
|
||||
createWriteStream(epsDtsPath, {
|
||||
flags: "w",
|
||||
}).write(content);
|
||||
}
|
||||
|
||||
@ -360,7 +381,7 @@ function createService() {
|
||||
// 分隔路径
|
||||
const arr = e.prefix
|
||||
.replace(/\//, "")
|
||||
.replace("admin", "")
|
||||
.replace(config.type, "")
|
||||
.split("/")
|
||||
.filter(Boolean)
|
||||
.map(toCamel);
|
||||
@ -382,32 +403,27 @@ function createService() {
|
||||
if (!d[k]) {
|
||||
d[k] = {
|
||||
namespace: e.prefix.substring(1, e.prefix.length),
|
||||
permission: {}
|
||||
permission: {},
|
||||
};
|
||||
}
|
||||
|
||||
// 创建方法
|
||||
e.api.forEach((a) => {
|
||||
// 方法名
|
||||
let n = a.path.replace("/", "");
|
||||
|
||||
if (n) {
|
||||
// 示例 /info/:id
|
||||
if (n.includes("/:")) {
|
||||
a.path = a.path.split("/:")[0];
|
||||
n = n.split("/:")[0];
|
||||
}
|
||||
const n = a.path.replace("/", "");
|
||||
|
||||
if (n && !/[-:]/g.test(n)) {
|
||||
d[k][n] = a;
|
||||
}
|
||||
});
|
||||
|
||||
// 创建权限
|
||||
getNames(d[k]).forEach((e) => {
|
||||
d[k].permission[e] = `${d[k].namespace.replace("admin/", "")}/${e}`.replace(
|
||||
/\//g,
|
||||
":"
|
||||
);
|
||||
d[k].permission[e] =
|
||||
`${d[k].namespace.replace(`${config.type}/`, "")}/${e}`.replace(
|
||||
/\//g,
|
||||
":",
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -426,7 +442,7 @@ export async function createEps(query?: { list: any[] }) {
|
||||
createService();
|
||||
|
||||
// 创建临时目录
|
||||
createDir(DistPath);
|
||||
createDir(DistDir);
|
||||
|
||||
// 创建 json 文件
|
||||
createJson();
|
||||
@ -436,6 +452,6 @@ export async function createEps(query?: { list: any[] }) {
|
||||
|
||||
return {
|
||||
service,
|
||||
list
|
||||
list,
|
||||
};
|
||||
}
|
12
packages/vite-plugin/src/index.ts
Normal file
12
packages/vite-plugin/src/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { base } from "./base";
|
||||
import { config } from "./config";
|
||||
import { demo } from "./demo";
|
||||
import type { Type } from "./types";
|
||||
import { virtual } from "./virtual";
|
||||
|
||||
export function cool(options: { type: Type; proxy: any; demo?: boolean }) {
|
||||
config.type = options.type;
|
||||
config.reqUrl = options.proxy["/dev/"].target;
|
||||
|
||||
return [base(), virtual(), demo(options.demo)];
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { createWriteStream } from "fs";
|
||||
import prettier from "prettier";
|
||||
import { join } from "path";
|
||||
import { mkdirs } from "../utils";
|
||||
import { mkdirs, rootDir } from "../utils";
|
||||
|
||||
// 创建文件
|
||||
export async function createMenu(options: { viewPath: string; code: string }) {
|
||||
@ -15,7 +15,7 @@ export async function createMenu(options: { viewPath: string; code: string }) {
|
||||
jsxBracketSameLine: true,
|
||||
singleQuote: false,
|
||||
printWidth: 100,
|
||||
trailingComma: "none"
|
||||
trailingComma: "none",
|
||||
});
|
||||
|
||||
// 目录路径
|
||||
@ -25,10 +25,10 @@ export async function createMenu(options: { viewPath: string; code: string }) {
|
||||
const fname = dir.pop();
|
||||
|
||||
// 创建目录
|
||||
const path = mkdirs(`./src/${dir.join("/")}`);
|
||||
const path = mkdirs(rootDir(`./src/${dir.join("/")}`));
|
||||
|
||||
// 创建文件
|
||||
createWriteStream(join(path, fname || "demo"), {
|
||||
flags: "w"
|
||||
flags: "w",
|
||||
}).write(content);
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
import { readFileSync, readdirSync } from "fs";
|
||||
import { extname } from "path";
|
||||
import { rootDir } from "../utils";
|
||||
|
||||
function findFiles(dir: string): string[] {
|
||||
const res: string[] = [];
|
||||
const dirs = readdirSync(dir, {
|
||||
withFileTypes: true
|
||||
withFileTypes: true,
|
||||
});
|
||||
for (const d of dirs) {
|
||||
if (d.isDirectory()) {
|
||||
@ -26,7 +27,7 @@ function findFiles(dir: string): string[] {
|
||||
height = s3;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
},
|
||||
);
|
||||
if (!/(viewBox="[^>+].*?")/g.test($2)) {
|
||||
content += `viewBox="0 0 ${width} ${height}"`;
|
||||
@ -42,13 +43,13 @@ function findFiles(dir: string): string[] {
|
||||
}
|
||||
|
||||
export function createSvg(html: string) {
|
||||
const res = findFiles("./src/");
|
||||
const res = findFiles(rootDir("./src/"));
|
||||
|
||||
return html.replace(
|
||||
"<body>",
|
||||
`<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
|
||||
${res.join("")}
|
||||
</svg>`
|
||||
</svg>`,
|
||||
);
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
export type Type = "app" | "admin";
|
||||
|
||||
export namespace Eps {
|
||||
interface Entity {
|
||||
api: {
|
||||
@ -31,3 +33,27 @@ export namespace Eps {
|
||||
[key: string]: any;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Ctx {
|
||||
type Pages = {
|
||||
path?: string;
|
||||
style?: {
|
||||
[key: string]: any;
|
||||
};
|
||||
[key: string]: any;
|
||||
}[];
|
||||
|
||||
type SubPackages = {
|
||||
root?: string;
|
||||
pages?: Pages;
|
||||
[key: string]: any;
|
||||
}[];
|
||||
|
||||
interface Data {
|
||||
appid?: string;
|
||||
pages?: Pages;
|
||||
subPackages?: SubPackages;
|
||||
modules?: string[];
|
||||
[key: string]: any;
|
||||
}
|
||||
}
|
@ -1,6 +1,17 @@
|
||||
import fs from "fs";
|
||||
import { join } from "path";
|
||||
import dayjs from "dayjs";
|
||||
import { config } from "../config";
|
||||
|
||||
// 根目录
|
||||
export function rootDir(path: string) {
|
||||
switch (config.type) {
|
||||
case "app":
|
||||
return join(process.env.UNI_INPUT_DIR!, path);
|
||||
|
||||
default:
|
||||
return join(process.cwd(), path);
|
||||
}
|
||||
}
|
||||
|
||||
// 首字母大写
|
||||
export function firstUpperCase(value: string): string {
|
||||
@ -22,10 +33,20 @@ export function createDir(path: string) {
|
||||
}
|
||||
|
||||
// 读取文件
|
||||
export function readFile(name: string) {
|
||||
export function readFile(path: string, json?: boolean) {
|
||||
try {
|
||||
return fs.readFileSync(name, "utf8");
|
||||
} catch (e) {}
|
||||
const content = fs.readFileSync(path, "utf8");
|
||||
return json ? JSON.parse(content) : content;
|
||||
} catch (err) {}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
export function writeFile(path: string, data: any) {
|
||||
try {
|
||||
return fs.writeFileSync(path, data);
|
||||
} catch (err) {}
|
||||
|
||||
return "";
|
||||
}
|
||||
@ -34,7 +55,7 @@ export function readFile(name: string) {
|
||||
export function parseJson(req: any): Promise<any> {
|
||||
return new Promise((resolve) => {
|
||||
let d = "";
|
||||
req.on("data", function (chunk: Buffer) {
|
||||
req.on("data", function (chunk: any) {
|
||||
d += chunk;
|
||||
});
|
||||
req.on("end", function () {
|
||||
@ -71,5 +92,9 @@ export function mkdirs(path: string) {
|
||||
}
|
||||
|
||||
export function error(message: string) {
|
||||
console.log("\x1B[31m%s\x1B[0m", `${dayjs().format("HH:mm:ss")} ${message || ""}`);
|
||||
console.log("\x1B[31m%s\x1B[0m", message);
|
||||
}
|
||||
|
||||
export function success(message: string) {
|
||||
console.log("\x1B[32m%s\x1B[0m", message);
|
||||
}
|
46
packages/vite-plugin/src/virtual.ts
Normal file
46
packages/vite-plugin/src/virtual.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import type { Plugin } from "vite";
|
||||
import { createEps } from "./eps";
|
||||
import { createCtx } from "./ctx";
|
||||
|
||||
export async function virtual(): Promise<Plugin> {
|
||||
const virtualModuleIds: string[] = ["virtual:eps", "virtual:ctx"];
|
||||
|
||||
const eps = await createEps();
|
||||
const ctx = await createCtx();
|
||||
|
||||
return {
|
||||
name: "vite-cool-virtual",
|
||||
enforce: "pre",
|
||||
handleHotUpdate({ file, server }) {
|
||||
if (!["pages.json", "dist"].some((e) => file.includes(e))) {
|
||||
createCtx();
|
||||
|
||||
createEps().then((data) => {
|
||||
// 通知客户端刷新
|
||||
server.ws.send({
|
||||
type: "custom",
|
||||
event: "eps-update",
|
||||
data,
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
resolveId(id) {
|
||||
if (virtualModuleIds.includes(id)) {
|
||||
return "\0" + id;
|
||||
}
|
||||
},
|
||||
load(id) {
|
||||
if (id === "\0virtual:eps") {
|
||||
return `
|
||||
export const eps = ${JSON.stringify(eps)}
|
||||
`;
|
||||
}
|
||||
if (id === "\0virtual:ctx") {
|
||||
return `
|
||||
export const ctx = ${JSON.stringify(ctx)}
|
||||
`;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
17
packages/vite-plugin/tsconfig.json
Normal file
17
packages/vite-plugin/tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"rootDir": "./",
|
||||
"types": ["node"],
|
||||
"target": "ESNext",
|
||||
"module": "CommonJS",
|
||||
"declaration": true,
|
||||
"outDir": "types",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"pretty": true,
|
||||
"resolveJsonModule": true,
|
||||
"typeRoots": ["./node_modules/@types/", "./src/types/"]
|
||||
},
|
||||
"include": ["src", "types.d.ts"],
|
||||
"exclude": ["dist"]
|
||||
}
|
6162
pnpm-lock.yaml
6162
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
import { type Module } from "../types";
|
||||
import { Module } from "../types";
|
||||
import { hmr } from "../hooks";
|
||||
import { dirs } from "virtual:module";
|
||||
import { ctx } from "virtual:ctx";
|
||||
|
||||
// 模块列表
|
||||
const list: Module[] = hmr.getData("modules", []);
|
||||
@ -8,7 +8,7 @@ const list: Module[] = hmr.getData("modules", []);
|
||||
// 模块对象
|
||||
const module = {
|
||||
list,
|
||||
dirs,
|
||||
dirs: ctx.modules,
|
||||
req: Promise.resolve(),
|
||||
get(name: string): Module {
|
||||
return this.list.find((e) => e.name == name)!;
|
||||
|
2
src/env.d.ts
vendored
2
src/env.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
/// <reference types="@cool-vue/crud/index.d.ts" />
|
||||
/// <reference types="../build/cool/dist/eps.d.ts" />
|
||||
/// <reference types="@cool-vue/vite-plugin/eps.d.ts" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_NAME: string;
|
||||
|
60
stats.html
60
stats.html
File diff suppressed because one or more lines are too long
@ -5,7 +5,7 @@ import vueJsx from "@vitejs/plugin-vue-jsx";
|
||||
import compression from "vite-plugin-compression";
|
||||
import { visualizer } from "rollup-plugin-visualizer";
|
||||
import { proxy } from "./src/config/proxy";
|
||||
import { cool } from "./build/cool";
|
||||
import { cool } from "@cool-vue/vite-plugin";
|
||||
|
||||
function resolve(dir: string) {
|
||||
return path.resolve(__dirname, ".", dir);
|
||||
@ -22,7 +22,11 @@ export default ({ mode }: ConfigEnv): UserConfig => {
|
||||
vue(),
|
||||
compression(),
|
||||
vueJsx(),
|
||||
cool(false), // 是否测试模式
|
||||
cool({
|
||||
type: "admin",
|
||||
proxy,
|
||||
demo: false // 是否测试模式
|
||||
}),
|
||||
visualizer({
|
||||
open: false,
|
||||
gzipSize: true,
|
||||
|
Loading…
Reference in New Issue
Block a user