index.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894
  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'), require('svgo')) :
  3. typeof define === 'function' && define.amd ? define(['exports', 'fs', 'path', 'axios', 'lodash', 'prettier', '@vue/compiler-sfc', 'magic-string', 'glob', 'svgo'], 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, global.svgo));
  5. })(this, (function (exports, fs, path, axios, lodash, prettier, compilerSfc, magicString, glob, svgo) { '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. // 创建文件
  531. async function createMenu(options) {
  532. // 格式化内容
  533. const content = await prettier.format(options.code, {
  534. parser: "vue",
  535. useTabs: true,
  536. tabWidth: 4,
  537. endOfLine: "lf",
  538. semi: true,
  539. jsxBracketSameLine: true,
  540. singleQuote: false,
  541. printWidth: 100,
  542. trailingComma: "none",
  543. });
  544. // 目录路径
  545. const dir = (options.viewPath || "").split("/");
  546. // 文件名
  547. const fname = dir.pop();
  548. // 源码路径
  549. const srcPath = `./src/${dir.join("/")}`;
  550. // 创建目录
  551. createDir(srcPath, true);
  552. // 创建文件
  553. fs.createWriteStream(path.join(srcPath, fname || "demo"), {
  554. flags: "w",
  555. }).write(content);
  556. }
  557. function base() {
  558. return {
  559. name: "vite-cool-base",
  560. enforce: "pre",
  561. configureServer(server) {
  562. server.middlewares.use(async (req, res, next) => {
  563. function done(data) {
  564. res.writeHead(200, { "Content-Type": "text/html;charset=UTF-8" });
  565. res.end(JSON.stringify(data));
  566. }
  567. if (req.originalUrl?.includes("__cool")) {
  568. const body = await parseJson(req);
  569. switch (req.url) {
  570. // 快速创建菜单
  571. case "/__cool_createMenu":
  572. await createMenu(body);
  573. break;
  574. // 创建描述文件
  575. case "/__cool_eps":
  576. await createEps(body);
  577. break;
  578. default:
  579. return done({
  580. code: 1001,
  581. message: "未知请求",
  582. });
  583. }
  584. done({
  585. code: 1000,
  586. });
  587. }
  588. else {
  589. next();
  590. }
  591. });
  592. },
  593. transform(code, id) {
  594. if (config.type == "admin") {
  595. return createTag(code, id);
  596. }
  597. return code;
  598. },
  599. };
  600. }
  601. function demo(enable) {
  602. const virtualModuleIds = ["virtual:demo"];
  603. return {
  604. name: "vite-cool-demo",
  605. enforce: "pre",
  606. resolveId(id) {
  607. if (virtualModuleIds.includes(id)) {
  608. return "\0" + id;
  609. }
  610. },
  611. async load(id) {
  612. if (id === "\0virtual:demo") {
  613. const demo = {};
  614. if (enable) {
  615. const files = await glob.glob(rootDir("./src/modules/demo/views/crud/components") + "/**", {
  616. stat: true,
  617. withFileTypes: true,
  618. });
  619. for (const file of files) {
  620. if (file.isFile()) {
  621. const p = path.join(file.path, file.name);
  622. demo[p
  623. .replace(/\\/g, "/")
  624. .split("src/modules/demo/views/crud/components/")[1]] = fs.readFileSync(p, "utf-8");
  625. }
  626. }
  627. }
  628. return `
  629. export const demo = ${JSON.stringify(demo)};
  630. `;
  631. }
  632. },
  633. };
  634. }
  635. async function createCtx() {
  636. let ctx = {
  637. serviceLang: "Node",
  638. };
  639. if (config.type == "app") {
  640. const manifest = readFile(rootDir("manifest.json"), true);
  641. // 文件路径
  642. const ctxPath = rootDir("pages.json");
  643. // 页面配置
  644. ctx = readFile(ctxPath, true);
  645. // 原数据,做更新比较用
  646. const ctxData = lodash.cloneDeep(ctx);
  647. // 删除临时页面
  648. ctx.pages = ctx.pages?.filter((e) => !e.isTemp);
  649. ctx.subPackages = ctx.subPackages?.filter((e) => !e.isTemp);
  650. // 加载 uni_modules 配置文件
  651. const files = await glob.glob(rootDir("uni_modules") + "/**/pages_init.json", {
  652. stat: true,
  653. withFileTypes: true,
  654. });
  655. for (const file of files) {
  656. if (file.isFile()) {
  657. const { pages = [], subPackages = [] } = readFile(path.join(file.path, file.name), true);
  658. // 合并到 pages 中
  659. [...pages, ...subPackages].forEach((e) => {
  660. e.isTemp = true;
  661. const isSub = !!e.root;
  662. const d = isSub
  663. ? ctx.subPackages?.find((a) => a.root == e.root)
  664. : ctx.pages?.find((a) => a.path == e.path);
  665. if (d) {
  666. lodash.assign(d, e);
  667. }
  668. else {
  669. if (isSub) {
  670. ctx.subPackages?.unshift(e);
  671. }
  672. else {
  673. ctx.pages?.unshift(e);
  674. }
  675. }
  676. });
  677. }
  678. }
  679. // 排序后检测,避免加载顺序问题
  680. function order(d) {
  681. return {
  682. pages: lodash.orderBy(d.pages, "path"),
  683. subPackages: lodash.orderBy(d.subPackages, "root"),
  684. };
  685. }
  686. // 是否需要更新 pages.json
  687. if (!lodash.isEqual(order(ctxData), order(ctx))) {
  688. console.log("[cool-ctx] pages updated");
  689. writeFile(ctxPath, JSON.stringify(ctx, null, 4));
  690. }
  691. // appid
  692. ctx.appid = manifest.appid;
  693. }
  694. if (config.type == "admin") {
  695. const list = fs.readdirSync(rootDir("./src/modules"));
  696. ctx.modules = list.filter((e) => !e.includes("."));
  697. await axios
  698. .get(config.reqUrl + "/admin/base/comm/program", {
  699. timeout: 5000,
  700. })
  701. .then((res) => {
  702. const { code, data, message } = res.data;
  703. if (code === 1000) {
  704. ctx.serviceLang = data || "Node";
  705. }
  706. else {
  707. error(`[cool-ctx] ${message}`);
  708. }
  709. })
  710. .catch((err) => {
  711. // console.error(['[cool-ctx] ', err.message])
  712. });
  713. }
  714. return ctx;
  715. }
  716. let svgIcons = [];
  717. function findSvg(dir) {
  718. const arr = [];
  719. const dirs = fs.readdirSync(dir, {
  720. withFileTypes: true,
  721. });
  722. for (const d of dirs) {
  723. if (d.isDirectory()) {
  724. arr.push(...findSvg(dir + d.name + "/"));
  725. }
  726. else {
  727. if (path.extname(d.name) == ".svg") {
  728. svgIcons.push(path.basename(d.name, ".svg"));
  729. const svg = fs.readFileSync(dir + d.name)
  730. .toString()
  731. .replace(/(\r)|(\n)/g, "")
  732. .replace(/<svg([^>+].*?)>/, (_, $2) => {
  733. let width = 0;
  734. let height = 0;
  735. let content = $2.replace(/(width|height)="([^>+].*?)"/g, (_, s2, s3) => {
  736. if (s2 === "width") {
  737. width = s3;
  738. }
  739. else if (s2 === "height") {
  740. height = s3;
  741. }
  742. return "";
  743. });
  744. if (!/(viewBox="[^>+].*?")/g.test($2)) {
  745. content += `viewBox="0 0 ${width} ${height}"`;
  746. }
  747. return `<symbol id="icon-${d.name.replace(".svg", "")}" ${content}>`;
  748. })
  749. .replace("</svg>", "</symbol>");
  750. arr.push(svg);
  751. }
  752. }
  753. }
  754. return arr;
  755. }
  756. function compilerSvg() {
  757. svgIcons = [];
  758. return findSvg(rootDir("./src/"))
  759. .map((e) => {
  760. return svgo.optimize(e)?.data || e;
  761. })
  762. .join("");
  763. }
  764. async function createSvg() {
  765. const html = compilerSvg();
  766. const code = `
  767. if (typeof window !== 'undefined') {
  768. function loadSvg() {
  769. const svgDom = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
  770. svgDom.style.position = 'absolute';
  771. svgDom.style.width = '0';
  772. svgDom.style.height = '0';
  773. svgDom.setAttribute('xmlns','http://www.w3.org/2000/svg');
  774. svgDom.setAttribute('xmlns:link','http://www.w3.org/1999/xlink');
  775. svgDom.innerHTML = '${html}';
  776. document.body.insertBefore(svgDom, document.body.firstChild);
  777. }
  778. loadSvg();
  779. }
  780. `;
  781. return { code, svgIcons };
  782. }
  783. async function virtual() {
  784. const virtualModuleIds = [
  785. "virtual:eps",
  786. "virtual:ctx",
  787. "virtual:svg-register",
  788. "virtual:svg-icons",
  789. ];
  790. return {
  791. name: "vite-cool-virtual",
  792. enforce: "pre",
  793. configureServer(server) {
  794. server.middlewares.use(async (req, res, next) => {
  795. // 页面刷新时触发
  796. if (req.url == "/@vite/client") {
  797. // 重新加载虚拟模块
  798. virtualModuleIds.forEach((vm) => {
  799. const mod = server.moduleGraph.getModuleById(`\0${vm}`);
  800. if (mod) {
  801. server.moduleGraph.invalidateModule(mod);
  802. }
  803. });
  804. }
  805. next();
  806. });
  807. },
  808. handleHotUpdate({ file, server }) {
  809. // 文件修改时触发
  810. if (!["pages.json", "dist", "build/cool", "eps.json", "eps.d.ts"].some((e) => file.includes(e))) {
  811. createCtx();
  812. createEps().then((data) => {
  813. if (data.isUpdate) {
  814. // 通知客户端刷新
  815. (server.hot || server.ws).send({
  816. type: "custom",
  817. event: "eps-update",
  818. data,
  819. });
  820. }
  821. });
  822. }
  823. },
  824. resolveId(id) {
  825. if (virtualModuleIds.includes(id)) {
  826. return "\0" + id;
  827. }
  828. },
  829. async load(id) {
  830. if (id === "\0virtual:eps") {
  831. const eps = await createEps();
  832. return `
  833. export const eps = ${JSON.stringify(eps)}
  834. `;
  835. }
  836. if (id === "\0virtual:ctx") {
  837. const ctx = await createCtx();
  838. return `
  839. export const ctx = ${JSON.stringify(ctx)}
  840. `;
  841. }
  842. if (id == "\0virtual:svg-register") {
  843. const { code } = await createSvg();
  844. return code;
  845. }
  846. if (id == "\0virtual:svg-icons") {
  847. const { svgIcons } = await createSvg();
  848. return `
  849. export const svgIcons = ${JSON.stringify(svgIcons)}
  850. `;
  851. }
  852. },
  853. };
  854. }
  855. function cool(options) {
  856. // 应用类型,admin | app
  857. config.type = options.type;
  858. // 请求地址
  859. config.reqUrl = options.proxy["/dev/"].target;
  860. // Eps
  861. if (options.eps) {
  862. const { dist, mapping, api } = options.eps;
  863. // 类型
  864. if (api) {
  865. config.eps.api = api;
  866. }
  867. // 输出目录
  868. if (dist) {
  869. config.eps.dist = dist;
  870. }
  871. // 匹配规则
  872. if (mapping) {
  873. lodash.merge(config.eps.mapping, mapping);
  874. }
  875. }
  876. return [base(), virtual(), demo(options.demo)];
  877. }
  878. exports.cool = cool;
  879. }));