index.js 32 KB

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