index.js 29 KB

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