@ybais/create-ybais-app 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +21 -0
  2. package/dist/index.js +371 -0
  3. package/package.json +34 -0
  4. package/template/.cursor/rules/ybais-app.mdc +21 -0
  5. package/template/.cursor/skills/ybais-app/SKILL.md +140 -0
  6. package/template/.cursor/skills/ybais-app/reference.md +180 -0
  7. package/template/.env +2 -0
  8. package/template/.env.prod +8 -0
  9. package/template/.env.test +8 -0
  10. package/template/.mise/tasks/build +107 -0
  11. package/template/.oxlintrc.json +22 -0
  12. package/template/AGENTS.md +25 -0
  13. package/template/README.md +251 -0
  14. package/template/dist/assets/css/index-bQrzgOlr.css +1 -0
  15. package/template/dist/assets/js/dist-CX33qWwE.js +2 -0
  16. package/template/dist/assets/js/dist-legacy-BnxRxjXx.js +1 -0
  17. package/template/dist/assets/js/index-BNqkt-js.js +2 -0
  18. package/template/dist/assets/js/index-legacy-DPO2FCfH.js +1 -0
  19. package/template/dist/assets/js/polyfills-Dsaw-v00.js +1 -0
  20. package/template/dist/assets/js/polyfills-legacy-DyZJWH2U.js +1 -0
  21. package/template/dist/assets/js/rolldown-runtime-LhxUO5vB.js +1 -0
  22. package/template/dist/assets/js/rolldown-runtime-legacy-BptCTa2h.js +1 -0
  23. package/template/dist/assets/js/routes-D2ibRZk6.js +1 -0
  24. package/template/dist/assets/js/routes-legacy-DGEWjKnS.js +1 -0
  25. package/template/dist/assets/js/vconsole.min-BcOaunN2.js +1 -0
  26. package/template/dist/assets/js/vconsole.min-legacy-Bub-moxG.js +1 -0
  27. package/template/dist/favicon.svg +1 -0
  28. package/template/dist/index.html +29 -0
  29. package/template/dist/version.json +4 -0
  30. package/template/index.html +19 -0
  31. package/template/mise.toml +11 -0
  32. package/template/package.json +60 -0
  33. package/template/plugins/app-version.ts +88 -0
  34. package/template/pnpm-lock.yaml +3359 -0
  35. package/template/pnpm-workspace.yaml +7 -0
  36. package/template/postcss.config.js +24 -0
  37. package/template/public/favicon.svg +1 -0
  38. package/template/scripts/run-build.mjs +5 -0
  39. package/template/scripts/setup-mise.mjs +47 -0
  40. package/template/scripts/with-mise.mjs +107 -0
  41. package/template/src/api/alova.d.ts +7 -0
  42. package/template/src/api/alova.ts +50 -0
  43. package/template/src/api/index.ts +8 -0
  44. package/template/src/api/qiniu.ts +8 -0
  45. package/template/src/api/uploader.ts +25 -0
  46. package/template/src/components/NotFoundComponent/index.tsx +48 -0
  47. package/template/src/components/RouterErrorComponent/index.tsx +27 -0
  48. package/template/src/components/index.ts +2 -0
  49. package/template/src/lib/README.md +17 -0
  50. package/template/src/main.tsx +64 -0
  51. package/template/src/routeTree.gen.ts +59 -0
  52. package/template/src/router.ts +10 -0
  53. package/template/src/routes/__root.tsx +10 -0
  54. package/template/src/routes/index.tsx +39 -0
  55. package/template/src/styles/index.css +42 -0
  56. package/template/src/styles/variant.css +18 -0
  57. package/template/src/styles/webview-compat.css +49 -0
  58. package/template/src/utils/cn.ts +63 -0
  59. package/template/src/vite-env.d.ts +41 -0
  60. package/template/tailwind.config.js +14 -0
  61. package/template/tsconfig.app.json +37 -0
  62. package/template/tsconfig.json +7 -0
  63. package/template/tsconfig.node.json +28 -0
  64. package/template/vite.config.ts +131 -0
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="48" height="46" fill="none" viewBox="0 0 48 46"><path fill="#863bff" d="M25.946 44.938c-.664.845-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.287c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.497 0-3.578-1.842-3.578H1.237c-.92 0-1.456-1.04-.92-1.788L10.013.474c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.579 1.842 3.579h11.377c.943 0 1.473 1.088.89 1.83L25.947 44.94z" style="fill:#863bff;fill:color(display-p3 .5252 .23 1);fill-opacity:1"/><mask id="a" width="48" height="46" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M25.842 44.938c-.664.844-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.183c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.498 0-3.579-1.842-3.579H1.133c-.92 0-1.456-1.04-.92-1.787L9.91.473c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.578 1.842 3.578h11.377c.943 0 1.473 1.088.89 1.832L25.843 44.94z" style="fill:#000;fill-opacity:1"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#ede6ff" rx="5.508" ry="14.704" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -4.47 31.516)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#ede6ff" rx="10.399" ry="29.851" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -39.328 7.883)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#7e14ff" rx="5.508" ry="30.487" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -25.913 -14.639)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -32.644 -3.334)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -34.34 30.47)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#ede6ff" rx="14.072" ry="22.078" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="rotate(93.35 24.506 48.493)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx=".387" cy="8.972" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(39.51 .387 8.972)"/></g><g filter="url(#k)"><ellipse cx="47.523" cy="-6.092" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 47.523 -6.092)"/></g><g filter="url(#l)"><ellipse cx="41.412" cy="6.333" fill="#47bfff" rx="5.971" ry="9.665" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 41.412 6.333)"/></g><g filter="url(#m)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#n)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#o)"><ellipse cx="35.651" cy="29.907" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 35.651 29.907)"/></g><g filter="url(#p)"><ellipse cx="38.418" cy="32.4" fill="#47bfff" rx="5.971" ry="15.297" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 38.418 32.4)"/></g></g><defs><filter id="b" width="60.045" height="41.654" x="-19.77" y="16.149" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-54.613" y="-7.533" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="d" width="79.355" height="29.4" x="-49.64" y="2.03" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="e" width="79.579" height="29.4" x="-45.045" y="20.029" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="f" width="79.579" height="29.4" x="-43.513" y="21.178" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="g" width="74.749" height="58.852" x="15.756" y="-17.901" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="h" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="i" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="j" width="56.045" height="63.649" x="-27.636" y="-22.853" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="k" width="54.814" height="64.646" x="20.116" y="-38.415" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="l" width="33.541" height="35.313" x="24.641" y="-11.323" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="m" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="n" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="o" width="54.814" height="64.646" x="8.244" y="-2.416" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="p" width="39.409" height="43.623" x="18.713" y="10.588" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter></defs></svg>
@@ -0,0 +1,29 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <script type="module" crossorigin src="./assets/js/polyfills-Dsaw-v00.js"></script>
5
+
6
+ <meta name="app-version" content="202609081648-594f883" />
7
+ <meta charset="UTF-8" />
8
+ <link rel="icon" type="image/svg+xml" href="./favicon.svg" />
9
+ <!-- viewport-fit=cover:开启异形屏/刘海屏全屏沉浸式渲染,启用 constant()/env() 安全区域变量 -->
10
+ <meta
11
+ name="viewport"
12
+ content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
13
+ />
14
+ <!-- 禁用移动端自动识别电话号码、邮箱地址和邮政地址 -->
15
+ <meta name="format-detection" content="telephone=no, email=no, adress=no" />
16
+ <title>ybais-app</title>
17
+ <script type="module" crossorigin src="./assets/js/index-BNqkt-js.js"></script>
18
+ <link rel="modulepreload" crossorigin href="./assets/js/rolldown-runtime-LhxUO5vB.js">
19
+ <link rel="stylesheet" crossorigin href="./assets/css/index-bQrzgOlr.css">
20
+ <script type="module">import'data:text/javascript,if(!import.meta.resolve)throw Error("import.meta.resolve not supported")';import.meta.url;import("_").catch(()=>1);(async function*(){})().next();window.__vite_is_modern_browser=true</script>
21
+ <script type="module">!function(){if(window.__vite_is_modern_browser)return;console.warn("vite: loading legacy chunks, syntax error above and the same error below should be ignored");var e=document.getElementById("vite-legacy-polyfill"),n=document.createElement("script");n.src=e.src,n.onload=function(){System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))},document.body.appendChild(n)}();</script>
22
+ </head>
23
+ <body>
24
+ <div id="root"></div>
25
+ <script nomodule>!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",(function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()}),!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();</script>
26
+ <script nomodule crossorigin id="vite-legacy-polyfill" src="./assets/js/polyfills-legacy-DyZJWH2U.js"></script>
27
+ <script nomodule crossorigin id="vite-legacy-entry" data-src="./assets/js/index-legacy-DPO2FCfH.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
28
+ </body>
29
+ </html>
@@ -0,0 +1,4 @@
1
+ {
2
+ "version": "202609081648-594f883",
3
+ "builtAt": "2026-09-08T08:48:20.447Z"
4
+ }
@@ -0,0 +1,19 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
+ <!-- viewport-fit=cover:开启异形屏/刘海屏全屏沉浸式渲染,启用 constant()/env() 安全区域变量 -->
7
+ <meta
8
+ name="viewport"
9
+ content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
10
+ />
11
+ <!-- 禁用移动端自动识别电话号码、邮箱地址和邮政地址 -->
12
+ <meta name="format-detection" content="telephone=no, email=no, adress=no" />
13
+ <title>ybais-app</title>
14
+ </head>
15
+ <body>
16
+ <div id="root"></div>
17
+ <script type="module" src="/src/main.tsx"></script>
18
+ </body>
19
+ </html>
@@ -0,0 +1,11 @@
1
+ # 构建任务实现见 .mise/tasks/build
2
+ # (TOML inline run 会与 bash ${...} 模板语法冲突,故使用 file task)
3
+ # 行为:master → prod,否则 test;构建前打印分支 / 模式 / 合并后环境变量
4
+ #
5
+ # 工具链:Node 26+、pnpm 11+(fuzzy major → 最新 26.x / 11.x)
6
+ # 下限细节见 package.json engines(Node >= 26.4.0、pnpm >= 11.9.0)。
7
+ # 浏览器/WebView 兼容由 @vitejs/plugin-legacy 等构建配置保证,与 Node 运行时版本无关。
8
+
9
+ [tools]
10
+ node = "26"
11
+ pnpm = "11"
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "ybais-app",
3
+ "private": true,
4
+ "version": "0.0.1",
5
+ "type": "module",
6
+ "sideEffects": [
7
+ "**/*.css",
8
+ "./src/styles/**/*.css",
9
+ "./src/main.tsx"
10
+ ],
11
+ "scripts": {
12
+ "dev": "vite --mode test --host",
13
+ "dev:test": "vite --mode test",
14
+ "dev:prod": "vite --mode prod",
15
+ "build": "node ./scripts/run-build.mjs",
16
+ "build:test": "tsc -b && vite build --mode test",
17
+ "build:prod": "tsc -b && vite build --mode prod",
18
+ "lint": "oxlint",
19
+ "preview": "pnpm run build && vite preview --host",
20
+ "postinstall": "node ./scripts/setup-mise.mjs"
21
+ },
22
+ "dependencies": {
23
+ "@tanstack/react-router": "^1.170.18",
24
+ "@ybais/alova": "workspace:*",
25
+ "@ybais/bridge": "workspace:*",
26
+ "@ybais/center-toast": "workspace:*",
27
+ "@ybais/clipboard": "workspace:*",
28
+ "@ybais/crypto": "workspace:*",
29
+ "@ybais/error-guard": "workspace:*",
30
+ "@ybais/qiniu-upload": "workspace:*",
31
+ "@ybais/updater": "workspace:*",
32
+ "@ybais/vconsole": "workspace:*",
33
+ "abortcontroller-polyfill": "1.7.8",
34
+ "alova": "^3.5.3",
35
+ "core-js": "^3.50.0",
36
+ "crypto-js": "^4.2.0",
37
+ "qiniu-js": "^3.4.4",
38
+ "react": "18.3.1",
39
+ "react-dom": "18.3.1",
40
+ "regenerator-runtime": "0.14.1",
41
+ "sonner": "^2.0.8",
42
+ "vconsole": "^3.15.1"
43
+ },
44
+ "devDependencies": {
45
+ "@tanstack/router-plugin": "^1.168.23",
46
+ "@types/crypto-js": "^4.2.2",
47
+ "@types/node": "^26.2.0",
48
+ "@types/react": "18.3.18",
49
+ "@types/react-dom": "18.3.5",
50
+ "@vitejs/plugin-legacy": "^8.2.2",
51
+ "@vitejs/plugin-react": "^6.0.4",
52
+ "autoprefixer": "^10.5.4",
53
+ "oxlint": "^1.77.0",
54
+ "postcss-px-to-viewport-8-plugin": "^1.2.5",
55
+ "tailwindcss": "3.4.19",
56
+ "terser": "^5.49.0",
57
+ "typescript": "~6.0.2",
58
+ "vite": "^8.1.5"
59
+ }
60
+ }
@@ -0,0 +1,88 @@
1
+ import { execSync } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import type { Plugin } from "vite";
5
+
6
+ export type AppVersionPayload = {
7
+ version: string;
8
+ builtAt: string;
9
+ };
10
+
11
+ function formatStamp(date = new Date()): string {
12
+ const y = date.getFullYear();
13
+ const m = String(date.getMonth() + 1).padStart(2, "0");
14
+ const d = String(date.getDate()).padStart(2, "0");
15
+ const h = String(date.getHours()).padStart(2, "0");
16
+ const min = String(date.getMinutes()).padStart(2, "0");
17
+ return `${y}${m}${d}${h}${min}`;
18
+ }
19
+
20
+ function tryGitShortHash(): string | null {
21
+ try {
22
+ return execSync("git rev-parse --short HEAD", {
23
+ stdio: ["ignore", "pipe", "ignore"],
24
+ })
25
+ .toString()
26
+ .trim();
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+
32
+ export function resolveAppVersion(): AppVersionPayload {
33
+ const builtAt = new Date().toISOString();
34
+ const fromEnv = process.env.APP_VERSION?.trim();
35
+ if (fromEnv) {
36
+ return { version: fromEnv, builtAt };
37
+ }
38
+ const stamp = formatStamp();
39
+ const hash = tryGitShortHash();
40
+ return {
41
+ version: hash ? `${stamp}-${hash}` : stamp,
42
+ builtAt,
43
+ };
44
+ }
45
+
46
+ /**
47
+ * 构建注入 app-version meta,并产出 version.json(含 dev middleware)
48
+ */
49
+ export function appVersionPlugin(
50
+ payload: AppVersionPayload = resolveAppVersion(),
51
+ ): Plugin {
52
+ const json = `${JSON.stringify(payload, null, 2)}\n`;
53
+
54
+ return {
55
+ name: "app-version",
56
+ transformIndexHtml(html) {
57
+ if (html.includes('name="app-version"')) {
58
+ return html.replace(
59
+ /<meta\s+name=["']app-version["']\s+content=["'][^"']*["']\s*\/?>/i,
60
+ `<meta name="app-version" content="${payload.version}" />`,
61
+ );
62
+ }
63
+ return html.replace(
64
+ /<head>/i,
65
+ `<head>\n <meta name="app-version" content="${payload.version}" />`,
66
+ );
67
+ },
68
+ configureServer(server) {
69
+ server.middlewares.use((req, res, next) => {
70
+ const url = req.url?.split("?")[0] ?? "";
71
+ if (url === "/version.json" || url.endsWith("/version.json")) {
72
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
73
+ res.setHeader("Cache-Control", "no-store");
74
+ res.end(json);
75
+ return;
76
+ }
77
+ next();
78
+ });
79
+ },
80
+ writeBundle(options) {
81
+ const outDir = options.dir;
82
+ if (!outDir) {
83
+ return;
84
+ }
85
+ fs.writeFileSync(path.join(outDir, "version.json"), json, "utf8");
86
+ },
87
+ };
88
+ }