index.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  1. (function (global, factory) {
  2. 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')) :
  3. typeof define === 'function' && define.amd ? define(['exports', 'fs', 'path', 'axios', 'lodash', 'prettier', '@vue/compiler-sfc', 'magic-string', 'glob'], factory) :
  4. (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));
  5. })(this, (function (exports, fs, path, axios, lodash, prettier, compilerSfc, magicString, glob) { 'use strict';
  6. const config = {
  7. type: "admin",
  8. reqUrl: "",
  9. demo: false,
  10. eps: {
  11. api: "",
  12. dist: "./build/cool",
  13. mapping: [
  14. {
  15. // 自定义匹配
  16. custom: ({ propertyName, type }) => {
  17. // 如果没有,返回null或者不返回,则继续遍历其他匹配规则
  18. return null;
  19. },
  20. },
  21. {
  22. type: "string",
  23. test: ["varchar", "text", "simple-json"],
  24. },
  25. {
  26. type: "string[]",
  27. test: ["simple-array"],
  28. },
  29. {
  30. type: "Date",
  31. test: ["datetime", "date"],
  32. },
  33. {
  34. type: "number",
  35. test: ["tinyint", "int", "decimal"],
  36. },
  37. {
  38. type: "BigInt",
  39. test: ["bigint"],
  40. },
  41. ],
  42. },
  43. };
  44. // 根目录
  45. function rootDir(path$1) {
  46. switch (config.type) {
  47. case "app":
  48. return path.join(process.env.UNI_INPUT_DIR, path$1);
  49. default:
  50. return path.join(process.cwd(), path$1);
  51. }
  52. }
  53. // 首字母大写
  54. function firstUpperCase(value) {
  55. return value.replace(/\b(\w)(\w*)/g, function ($0, $1, $2) {
  56. return $1.toUpperCase() + $2;
  57. });
  58. }
  59. // 横杠转驼峰
  60. function toCamel(str) {
  61. return str.replace(/([^-])(?:-+([^-]))/g, function ($0, $1, $2) {
  62. return $1 + $2.toUpperCase();
  63. });
  64. }
  65. // 创建目录
  66. function createDir(path, recursive) {
  67. try {
  68. if (!fs.existsSync(path))
  69. fs.mkdirSync(path, { recursive });
  70. }
  71. catch (err) { }
  72. }
  73. // 读取文件
  74. function readFile(path, json) {
  75. try {
  76. const content = fs.readFileSync(path, "utf8");
  77. return json ? JSON.parse(content) : content;
  78. }
  79. catch (err) { }
  80. return "";
  81. }
  82. // 写入文件
  83. function writeFile(path, data) {
  84. try {
  85. return fs.writeFileSync(path, data);
  86. }
  87. catch (err) { }
  88. return "";
  89. }
  90. // 解析body
  91. function parseJson(req) {
  92. return new Promise((resolve) => {
  93. let d = "";
  94. req.on("data", function (chunk) {
  95. d += chunk;
  96. });
  97. req.on("end", function () {
  98. try {
  99. resolve(JSON.parse(d));
  100. }
  101. catch {
  102. resolve({});
  103. }
  104. });
  105. });
  106. }
  107. function error(message) {
  108. console.log("\x1B[31m%s\x1B[0m", message);
  109. }
  110. let service = {};
  111. let list = [];
  112. let customList = [];
  113. // 获取请求地址
  114. function getEpsUrl() {
  115. let url = config.eps.api;
  116. if (!url) {
  117. url = config.type;
  118. }
  119. switch (url) {
  120. case "app":
  121. url = "/app/base/comm/eps";
  122. break;
  123. case "admin":
  124. url = "/admin/base/open/eps";
  125. break;
  126. }
  127. return url;
  128. }
  129. // 获取路径
  130. function getEpsPath(filename) {
  131. return path.join(config.type == "admin" ? config.eps.dist : rootDir(config.eps.dist), filename || "");
  132. }
  133. // 获取方法名
  134. function getNames(v) {
  135. return Object.keys(v).filter((e) => !["namespace", "permission"].includes(e));
  136. }
  137. // 获取数据
  138. async function getData(data) {
  139. // 自定义数据
  140. if (!lodash.isEmpty(data)) {
  141. customList = (data || []).map((e) => {
  142. return {
  143. ...e,
  144. isLocal: true,
  145. };
  146. });
  147. }
  148. // 本地文件
  149. const epsPath = getEpsPath("eps.json");
  150. try {
  151. list = readFile(epsPath, true) || [];
  152. }
  153. catch (err) {
  154. error(`[cool-eps] ${epsPath} 文件异常, ${err.message}`);
  155. }
  156. // 请求地址
  157. const url = config.reqUrl + getEpsUrl();
  158. // 请求数据
  159. await axios
  160. .get(url, {
  161. timeout: 5000,
  162. })
  163. .then((res) => {
  164. const { code, data, message } = res.data;
  165. if (code === 1000) {
  166. if (!lodash.isEmpty(data) && data) {
  167. list = lodash.values(data).flat();
  168. }
  169. }
  170. else {
  171. error(`[cool-eps] ${message}`);
  172. }
  173. })
  174. .catch(() => {
  175. error(`[cool-eps] 后端未启动 ➜ ${url}`);
  176. });
  177. // 合并自定义数据
  178. if (lodash.isArray(customList)) {
  179. customList.forEach((e) => {
  180. const d = list.find((a) => e.prefix === a.prefix);
  181. if (d) {
  182. lodash.merge(d, e);
  183. }
  184. else {
  185. list.push(e);
  186. }
  187. });
  188. }
  189. // 设置默认值
  190. list.forEach((e) => {
  191. if (!e.namespace) {
  192. e.namespace = "";
  193. }
  194. if (!e.api) {
  195. e.api = [];
  196. }
  197. if (!e.columns) {
  198. e.columns = [];
  199. }
  200. });
  201. }
  202. // 创建 json 文件
  203. function createJson() {
  204. const arr = list.map((e) => {
  205. return {
  206. prefix: e.prefix,
  207. name: e.name || "",
  208. api: e.api.map((e) => {
  209. return {
  210. name: e.name,
  211. method: e.method,
  212. path: e.path,
  213. };
  214. }),
  215. };
  216. });
  217. const content = JSON.stringify(arr);
  218. const local_content = readFile(getEpsPath("eps.json"));
  219. // 是否需要更新
  220. const isUpdate = content != local_content;
  221. if (isUpdate) {
  222. fs.createWriteStream(getEpsPath("eps.json"), {
  223. flags: "w",
  224. }).write(content);
  225. }
  226. return isUpdate;
  227. }
  228. // 创建描述文件
  229. async function createDescribe({ list, service }) {
  230. // 获取类型
  231. function getType({ propertyName, type }) {
  232. for (const map of config.eps.mapping) {
  233. if (map.custom) {
  234. const resType = map.custom({ propertyName, type });
  235. if (resType)
  236. return resType;
  237. }
  238. if (map.test) {
  239. if (map.test.includes(type))
  240. return map.type;
  241. }
  242. }
  243. return type;
  244. }
  245. // 创建 Entity
  246. function createEntity() {
  247. const t0 = [];
  248. const arr = [];
  249. for (const item of list) {
  250. if (!item.name)
  251. continue;
  252. const t = [`interface ${item.name} {`];
  253. for (const col of item.columns || []) {
  254. // 描述
  255. t.push("\n");
  256. t.push("/**\n");
  257. t.push(` * ${col.comment}\n`);
  258. t.push(" */\n");
  259. t.push(`${col.propertyName}?: ${getType({
  260. propertyName: col.propertyName,
  261. type: col.type,
  262. })};`);
  263. }
  264. t.push("\n");
  265. t.push("/**\n");
  266. t.push(` * 任意键值\n`);
  267. t.push(" */\n");
  268. t.push(`[key: string]: any;`);
  269. t.push("}");
  270. if (!arr.includes(item.name)) {
  271. arr.push(item.name);
  272. t0.push(t);
  273. }
  274. }
  275. return t0.map((e) => e.join("")).join("\n\n");
  276. }
  277. // 创建 Service
  278. function createDts() {
  279. const t0 = [];
  280. const t1 = [
  281. `
  282. type json = any;
  283. type Service = {
  284. request(options?: {
  285. url: string;
  286. method?: "POST" | "GET" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS";
  287. data?: any;
  288. params?: any;
  289. headers?: {
  290. [key: string]: any;
  291. },
  292. timeout?: number;
  293. proxy?: boolean;
  294. [key: string]: any;
  295. }): Promise<any>;
  296. `,
  297. ];
  298. // 处理数据
  299. function deep(d, k) {
  300. if (!k)
  301. k = "";
  302. for (const i in d) {
  303. const name = k + toCamel(firstUpperCase(i.replace(/[:]/g, "")));
  304. if (d[i].namespace) {
  305. // 查找配置
  306. const item = list.find((e) => (e.prefix || "") === `/${d[i].namespace}`);
  307. if (item) {
  308. const t = [`interface ${name} {`];
  309. t1.push(`${i}: ${name};`);
  310. // 插入方法
  311. if (item.api) {
  312. // 权限列表
  313. const permission = [];
  314. item.api.forEach((a) => {
  315. // 方法名
  316. const n = toCamel(a.name || lodash.last(a.path.split("/")) || "").replace(/[:\/-]/g, "");
  317. if (n) {
  318. // 参数类型
  319. let q = [];
  320. // 参数列表
  321. const { parameters = [] } = a.dts || {};
  322. parameters.forEach((p) => {
  323. if (p.description) {
  324. q.push(`\n/** ${p.description} */\n`);
  325. }
  326. if (p.name.includes(":")) {
  327. return false;
  328. }
  329. const a = `${p.name}${p.required ? "" : "?"}`;
  330. const b = `${p.schema.type || "string"}`;
  331. q.push(`${a}: ${b},`);
  332. });
  333. if (lodash.isEmpty(q)) {
  334. q = ["any"];
  335. }
  336. else {
  337. q.unshift("{");
  338. q.push("}");
  339. }
  340. // 返回类型
  341. let res = "";
  342. // 实体名
  343. const en = item.name || "any";
  344. switch (a.path) {
  345. case "/page":
  346. res = `
  347. {
  348. pagination: { size: number; page: number; total: number; [key: string]: any };
  349. list: ${en} [];
  350. [key: string]: any;
  351. }
  352. `;
  353. break;
  354. case "/list":
  355. res = `${en} []`;
  356. break;
  357. case "/info":
  358. res = en;
  359. break;
  360. default:
  361. res = "any";
  362. break;
  363. }
  364. // 描述
  365. t.push("\n");
  366. t.push("/**\n");
  367. t.push(` * ${a.summary || n}\n`);
  368. t.push(" */\n");
  369. t.push(`${n}(data${q.length == 1 ? "?" : ""}: ${q.join("")}): Promise<${res}>;`);
  370. if (!permission.includes(n)) {
  371. permission.push(n);
  372. }
  373. }
  374. });
  375. // 权限标识
  376. t.push("\n");
  377. t.push("/**\n");
  378. t.push(" * 权限标识\n");
  379. t.push(" */\n");
  380. t.push(`permission: { ${permission
  381. .map((e) => `${e}: string;`)
  382. .join("\n")} };`);
  383. // 权限状态
  384. t.push("\n");
  385. t.push("/**\n");
  386. t.push(" * 权限状态\n");
  387. t.push(" */\n");
  388. t.push(`_permission: { ${permission
  389. .map((e) => `${e}: boolean;`)
  390. .join("\n")} };`);
  391. // 请求
  392. t.push("\n");
  393. t.push("/**\n");
  394. t.push(" * 请求\n");
  395. t.push(" */\n");
  396. t.push(`request: Service['request']`);
  397. }
  398. t.push("}");
  399. t0.push(t);
  400. }
  401. }
  402. else {
  403. t1.push(`${i}: {`);
  404. deep(d[i], name);
  405. t1.push(`},`);
  406. }
  407. }
  408. }
  409. // 深度
  410. deep(service);
  411. // 结束
  412. t1.push("}");
  413. // 追加
  414. t0.push(t1);
  415. return t0.map((e) => e.join("")).join("\n\n");
  416. }
  417. // 文件内容
  418. const text = `
  419. declare namespace Eps {
  420. ${createEntity()}
  421. ${createDts()}
  422. }
  423. `;
  424. // 文本内容
  425. const content = await prettier.format(text, {
  426. parser: "typescript",
  427. useTabs: true,
  428. tabWidth: 4,
  429. endOfLine: "lf",
  430. semi: true,
  431. singleQuote: false,
  432. printWidth: 100,
  433. trailingComma: "none",
  434. });
  435. const local_content = readFile(getEpsPath("eps.d.ts"));
  436. // 是否需要更新
  437. if (content != local_content) {
  438. // 创建 eps 描述文件
  439. fs.createWriteStream(getEpsPath("eps.d.ts"), {
  440. flags: "w",
  441. }).write(content);
  442. }
  443. }
  444. // 创建 service
  445. function createService() {
  446. // 路径第一层作为 id 标识
  447. const id = getEpsUrl().split("/")[1];
  448. list.forEach((e) => {
  449. // 请求地址
  450. const path = e.prefix[0] == "/" ? e.prefix.substring(1, e.prefix.length) : e.prefix;
  451. // 分隔路径
  452. const arr = path.replace(id, "").split("/").filter(Boolean).map(toCamel);
  453. // 遍历
  454. function deep(d, i) {
  455. const k = arr[i];
  456. if (k) {
  457. // 是否最后一个
  458. if (arr[i + 1]) {
  459. if (!d[k]) {
  460. d[k] = {};
  461. }
  462. deep(d[k], i + 1);
  463. }
  464. else {
  465. // 不存在则创建
  466. if (!d[k]) {
  467. d[k] = {
  468. namespace: path,
  469. permission: {},
  470. };
  471. }
  472. // 创建方法
  473. e.api.forEach((a) => {
  474. // 方法名
  475. const n = a.path.replace("/", "");
  476. if (n && !/[-:]/g.test(n)) {
  477. d[k][n] = a;
  478. }
  479. });
  480. // 创建权限
  481. getNames(d[k]).forEach((i) => {
  482. d[k].permission[i] = `${d[k].namespace.replace(`${id}/`, "")}/${i}`.replace(/\//g, ":");
  483. });
  484. }
  485. }
  486. }
  487. deep(service, 0);
  488. });
  489. }
  490. // 创建 eps
  491. async function createEps(query) {
  492. // 获取数据
  493. await getData(query?.list || []);
  494. // 创建 service
  495. createService();
  496. // 创建目录
  497. createDir(getEpsPath(), true);
  498. // 创建 json 文件
  499. const isUpdate = createJson();
  500. // 创建描述文件
  501. createDescribe({ service, list });
  502. return {
  503. service,
  504. list,
  505. isUpdate,
  506. };
  507. }
  508. function createTag(code, id) {
  509. if (/\.vue$/.test(id)) {
  510. let s;
  511. const str = () => s || (s = new magicString(code));
  512. const { descriptor } = compilerSfc.parse(code);
  513. if (!descriptor.script && descriptor.scriptSetup) {
  514. const res = compilerSfc.compileScript(descriptor, { id });
  515. const { name, lang } = res.attrs;
  516. str().appendLeft(0, `<script lang="${lang}">
  517. import { defineComponent } from 'vue'
  518. export default defineComponent({
  519. name: "${name}"
  520. })
  521. <\/script>`);
  522. return {
  523. map: str().generateMap(),
  524. code: str().toString()
  525. };
  526. }
  527. }
  528. return null;
  529. }
  530. function findFiles(dir) {
  531. const res = [];
  532. const dirs = fs.readdirSync(dir, {
  533. withFileTypes: true,
  534. });
  535. for (const d of dirs) {
  536. if (d.isDirectory()) {
  537. res.push(...findFiles(dir + d.name + "/"));
  538. }
  539. else {
  540. if (path.extname(d.name) == ".svg") {
  541. const svg = fs.readFileSync(dir + d.name)
  542. .toString()
  543. .replace(/(\r)|(\n)/g, "")
  544. .replace(/<svg([^>+].*?)>/, (_, $2) => {
  545. let width = 0;
  546. let height = 0;
  547. let content = $2.replace(/(width|height)="([^>+].*?)"/g, (_, s2, s3) => {
  548. if (s2 === "width") {
  549. width = s3;
  550. }
  551. else if (s2 === "height") {
  552. height = s3;
  553. }
  554. return "";
  555. });
  556. if (!/(viewBox="[^>+].*?")/g.test($2)) {
  557. content += `viewBox="0 0 ${width} ${height}"`;
  558. }
  559. return `<symbol id="icon-${d.name.replace(".svg", "")}" ${content}>`;
  560. })
  561. .replace("</svg>", "</symbol>");
  562. res.push(svg);
  563. }
  564. }
  565. }
  566. return res;
  567. }
  568. function createSvg(html) {
  569. const res = findFiles(rootDir("./src/"));
  570. return html.replace("<body>", `<body>
  571. <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
  572. ${res.join("")}
  573. </svg>`);
  574. }
  575. // 创建文件
  576. async function createMenu(options) {
  577. // 格式化内容
  578. const content = await prettier.format(options.code, {
  579. parser: "vue",
  580. useTabs: true,
  581. tabWidth: 4,
  582. endOfLine: "lf",
  583. semi: true,
  584. jsxBracketSameLine: true,
  585. singleQuote: false,
  586. printWidth: 100,
  587. trailingComma: "none",
  588. });
  589. // 目录路径
  590. const dir = (options.viewPath || "").split("/");
  591. // 文件名
  592. const fname = dir.pop();
  593. // 源码路径
  594. const srcPath = `./src/${dir.join("/")}`;
  595. // 创建目录
  596. createDir(srcPath, true);
  597. // 创建文件
  598. fs.createWriteStream(path.join(srcPath, fname || "demo"), {
  599. flags: "w",
  600. }).write(content);
  601. }
  602. function base() {
  603. return {
  604. name: "vite-cool-base",
  605. enforce: "pre",
  606. configureServer(server) {
  607. server.middlewares.use(async (req, res, next) => {
  608. function done(data) {
  609. res.writeHead(200, { "Content-Type": "text/html;charset=UTF-8" });
  610. res.end(JSON.stringify(data));
  611. }
  612. if (req.originalUrl?.includes("__cool")) {
  613. const body = await parseJson(req);
  614. switch (req.url) {
  615. // 快速创建菜单
  616. case "/__cool_createMenu":
  617. await createMenu(body);
  618. break;
  619. // 创建描述文件
  620. case "/__cool_eps":
  621. await createEps(body);
  622. break;
  623. default:
  624. return done({
  625. code: 1001,
  626. message: "未知请求",
  627. });
  628. }
  629. done({
  630. code: 1000,
  631. });
  632. }
  633. else {
  634. next();
  635. }
  636. });
  637. },
  638. transform(code, id) {
  639. if (config.type == "admin") {
  640. return createTag(code, id);
  641. }
  642. return code;
  643. },
  644. transformIndexHtml(html) {
  645. if (config.type == "admin") {
  646. return createSvg(html);
  647. }
  648. return html;
  649. },
  650. };
  651. }
  652. function demo(enable) {
  653. const virtualModuleIds = ["virtual:demo"];
  654. return {
  655. name: "vite-cool-demo",
  656. enforce: "pre",
  657. resolveId(id) {
  658. if (virtualModuleIds.includes(id)) {
  659. return "\0" + id;
  660. }
  661. },
  662. async load(id) {
  663. if (id === "\0virtual:demo") {
  664. const demo = {};
  665. if (enable) {
  666. const files = await glob.glob(rootDir("./src/modules/demo/views/crud/components") + "/**", {
  667. stat: true,
  668. withFileTypes: true,
  669. });
  670. for (const file of files) {
  671. if (file.isFile()) {
  672. const p = path.join(file.path, file.name);
  673. demo[p
  674. .replace(/\\/g, "/")
  675. .split("src/modules/demo/views/crud/components/")[1]] = fs.readFileSync(p, "utf-8");
  676. }
  677. }
  678. }
  679. return `
  680. export const demo = ${JSON.stringify(demo)};
  681. `;
  682. }
  683. },
  684. };
  685. }
  686. async function createCtx() {
  687. let ctx = {
  688. serviceLang: "Node",
  689. };
  690. if (config.type == "app") {
  691. const manifest = readFile(rootDir("manifest.json"), true);
  692. // 文件路径
  693. const ctxPath = rootDir("pages.json");
  694. // 页面配置
  695. ctx = readFile(ctxPath, true);
  696. // 原数据,做更新比较用
  697. const ctxData = lodash.cloneDeep(ctx);
  698. // 删除临时页面
  699. ctx.pages = ctx.pages?.filter((e) => !e.isTemp);
  700. ctx.subPackages = ctx.subPackages?.filter((e) => !e.isTemp);
  701. // 加载 uni_modules 配置文件
  702. const files = await glob.glob(rootDir("uni_modules") + "/**/pages_init.json", {
  703. stat: true,
  704. withFileTypes: true,
  705. });
  706. for (const file of files) {
  707. if (file.isFile()) {
  708. const { pages = [], subPackages = [] } = readFile(path.join(file.path, file.name), true);
  709. // 合并到 pages 中
  710. [...pages, ...subPackages].forEach((e) => {
  711. e.isTemp = true;
  712. const isSub = !!e.root;
  713. const d = isSub
  714. ? ctx.subPackages?.find((a) => a.root == e.root)
  715. : ctx.pages?.find((a) => a.path == e.path);
  716. if (d) {
  717. lodash.assign(d, e);
  718. }
  719. else {
  720. if (isSub) {
  721. ctx.subPackages?.unshift(e);
  722. }
  723. else {
  724. ctx.pages?.unshift(e);
  725. }
  726. }
  727. });
  728. }
  729. }
  730. // 排序后检测,避免加载顺序问题
  731. function order(d) {
  732. return {
  733. pages: lodash.orderBy(d.pages, "path"),
  734. subPackages: lodash.orderBy(d.subPackages, "root"),
  735. };
  736. }
  737. // 是否需要更新 pages.json
  738. if (!lodash.isEqual(order(ctxData), order(ctx))) {
  739. console.log("[cool-ctx] pages updated");
  740. writeFile(ctxPath, JSON.stringify(ctx, null, 4));
  741. }
  742. // appid
  743. ctx.appid = manifest.appid;
  744. }
  745. if (config.type == "admin") {
  746. const list = fs.readdirSync(rootDir("./src/modules"));
  747. ctx.modules = list.filter((e) => !e.includes("."));
  748. await axios
  749. .get(config.reqUrl + "/admin/base/comm/program", {
  750. timeout: 5000,
  751. })
  752. .then((res) => {
  753. const { code, data, message } = res.data;
  754. if (code === 1000) {
  755. ctx.serviceLang = data || "Node";
  756. }
  757. else {
  758. error(`[cool-ctx] ${message}`);
  759. }
  760. })
  761. .catch((err) => {
  762. // console.error(['[cool-ctx] ', err.message])
  763. });
  764. }
  765. return ctx;
  766. }
  767. async function virtual() {
  768. const virtualModuleIds = ["virtual:eps", "virtual:ctx"];
  769. return {
  770. name: "vite-cool-virtual",
  771. enforce: "pre",
  772. configureServer(server) {
  773. server.middlewares.use(async (req, res, next) => {
  774. // 页面刷新时触发
  775. if (req.url == "/@vite/client") {
  776. // 重新加载虚拟模块
  777. virtualModuleIds.forEach((vm) => {
  778. const mod = server.moduleGraph.getModuleById(`\0${vm}`);
  779. if (mod) {
  780. server.moduleGraph.invalidateModule(mod);
  781. }
  782. });
  783. }
  784. next();
  785. });
  786. },
  787. handleHotUpdate({ file, server }) {
  788. // 文件修改时触发
  789. if (!["pages.json", "dist", "build/cool", "eps.json", "eps.d.ts"].some((e) => file.includes(e))) {
  790. createCtx();
  791. createEps().then((data) => {
  792. if (data.isUpdate) {
  793. // 通知客户端刷新
  794. (server.hot || server.ws).send({
  795. type: "custom",
  796. event: "eps-update",
  797. data,
  798. });
  799. }
  800. });
  801. }
  802. },
  803. resolveId(id) {
  804. if (virtualModuleIds.includes(id)) {
  805. return "\0" + id;
  806. }
  807. },
  808. async load(id) {
  809. if (id === "\0virtual:eps") {
  810. const eps = await createEps();
  811. return `
  812. export const eps = ${JSON.stringify(eps)}
  813. `;
  814. }
  815. if (id === "\0virtual:ctx") {
  816. const ctx = await createCtx();
  817. return `
  818. export const ctx = ${JSON.stringify(ctx)}
  819. `;
  820. }
  821. },
  822. };
  823. }
  824. function cool(options) {
  825. // 应用类型,admin | app
  826. config.type = options.type;
  827. // 请求地址
  828. config.reqUrl = options.proxy["/dev/"].target;
  829. // Eps
  830. if (options.eps) {
  831. const { dist, mapping, api } = options.eps;
  832. // 类型
  833. if (api) {
  834. config.eps.api = api;
  835. }
  836. // 输出目录
  837. if (dist) {
  838. config.eps.dist = dist;
  839. }
  840. // 匹配规则
  841. if (mapping) {
  842. lodash.merge(config.eps.mapping, mapping);
  843. }
  844. }
  845. return [base(), virtual(), demo(options.demo)];
  846. }
  847. exports.cool = cool;
  848. }));