33 Commits

Author SHA1 Message Date
330a3aa916 chore(deps): update all non-major dependencies
Some checks failed
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Failing after 1m54s
CI/CD Pipeline / build-and-deploy (push) Has been skipped
2026-01-03 19:51:22 +08:00
16d4b3f749 chore(deps): update primevue pkgs to v4.5.4
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m38s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m57s
测试最新依赖 / build-and-test (push) Successful in 1m43s
测试最新依赖 / playwright (push) Successful in 1m54s
2026-01-01 22:28:04 +08:00
42f63f7134 chore(deps): update dependency unplugin-vue-router to v0.19.2
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m7s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m51s
2026-01-01 19:08:32 +08:00
9d12927793 chore(deps): update dependency @pinia/colada to ^0.19.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m59s
测试最新依赖 / playwright (push) Successful in 1m59s
测试最新依赖 / build-and-test (push) Successful in 2m7s
2025-12-31 06:25:53 +08:00
bb82a6ab83 chore(deps): update dependency @vitejs/plugin-vue-jsx to v5.1.3
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m53s
测试最新依赖 / playwright (push) Successful in 1m41s
测试最新依赖 / build-and-test (push) Successful in 1m51s
2025-12-27 12:15:40 +08:00
76d2a45015 chore(deps): update primevue pkgs to v4.5.3
Some checks failed
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m10s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m57s
测试最新依赖 / playwright (push) Failing after 1m2s
测试最新依赖 / build-and-test (push) Failing after 1m28s
2025-12-24 23:32:51 +08:00
严浩
935251ee53 chore(devcontainer): update configuration and lifecycle scripts
All checks were successful
CI/CD Pipeline / playwright (push) Successful in 1m43s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m14s
测试最新依赖 / playwright (push) Successful in 1m46s
测试最新依赖 / build-and-test (push) Successful in 2m6s
2025-12-24 00:19:05 +08:00
5472f1c9b9 chore(deps): update dependency eslint-plugin-perfectionist to v5
All checks were successful
CI/CD Pipeline / playwright (push) Successful in 2m49s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m51s
测试最新依赖 / build-and-test (push) Successful in 1m54s
测试最新依赖 / playwright (push) Successful in 1m58s
2025-12-23 01:06:20 +08:00
744703811a chore(deps): update primevue pkgs to v4.5.2
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m38s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m53s
2025-12-22 19:25:58 +08:00
be3d59691b chore(deps): update dependency primelocale to v2.2.3
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m38s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m47s
测试最新依赖 / build-and-test (push) Successful in 1m35s
测试最新依赖 / playwright (push) Successful in 1m46s
2025-12-19 04:51:01 +08:00
f6bccd7536 chore(deps): update dependency unplugin-vue-router to v0.19.1
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m45s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m46s
测试最新依赖 / build-and-test (push) Successful in 1m59s
测试最新依赖 / playwright (push) Successful in 2m28s
2025-12-18 23:35:22 +08:00
4673d622b6 chore(deps): update dependency vite to v7.3.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m18s
2025-12-18 20:56:11 +08:00
0e467a1e9d chore(deps): update primevue pkgs to v4.5.1
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m44s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m15s
测试最新依赖 / build-and-test (push) Successful in 1m28s
测试最新依赖 / playwright (push) Successful in 2m16s
2025-12-17 23:51:43 +08:00
7a5610c0dc chore(deps): update all non-major dependencies
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m35s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m51s
测试最新依赖 / playwright (push) Successful in 1m42s
测试最新依赖 / build-and-test (push) Successful in 2m9s
2025-12-17 03:32:37 +08:00
80d989f6d0 chore(deps): update dependency @pinia/colada to ^0.18.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m49s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m54s
2025-12-17 00:53:21 +08:00
6097d39752 chore(deps): update dependency @primeuix/themes to v2
All checks were successful
CI/CD Pipeline / playwright (push) Successful in 2m28s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m57s
2025-12-16 23:17:01 +08:00
4bc62221b6 chore(deps): update dependency @vitejs/plugin-vue to v6.0.3
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m37s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m54s
测试最新依赖 / build-and-test (push) Successful in 1m33s
测试最新依赖 / playwright (push) Successful in 2m22s
2025-12-15 17:45:16 +08:00
c405bc9481 chore(deps): update dependency @intlify/unplugin-vue-i18n to v11.0.3
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m44s
测试最新依赖 / build-and-test (push) Successful in 1m44s
测试最新依赖 / playwright (push) Successful in 2m22s
2025-12-14 21:56:20 +08:00
54cc46b33a chore(deps): update all non-major dependencies
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m36s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m58s
测试最新依赖 / build-and-test (push) Successful in 1m29s
测试最新依赖 / playwright (push) Successful in 2m26s
2025-12-12 23:14:48 +08:00
65ee7cccb3 chore(deps): update dependency happy-dom to v20.0.11
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m45s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m12s
2025-12-12 09:52:28 +08:00
6da6e59b6d chore(deps): update primevue pkgs to v4.5.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m10s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m54s
2025-12-12 03:56:01 +08:00
8e6d7488f4 chore(deps): update dependency vite-plugin-checker to ^0.12.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m5s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m55s
测试最新依赖 / build-and-test (push) Successful in 1m32s
测试最新依赖 / playwright (push) Successful in 2m22s
2025-12-12 01:36:17 +08:00
严浩
9e050306bb feat: refactor Vite plugin loading mechanism and improve plugin management
All checks were successful
CI/CD Pipeline / playwright (push) Successful in 3m3s
CI/CD Pipeline / build-and-deploy (push) Successful in 2m6s
2025-12-12 00:09:53 +08:00
7f1811098f 更新 .github/workflows/测试最新依赖.yaml
All checks were successful
CI/CD Pipeline / playwright (push) Successful in 1m37s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m46s
2025-12-11 21:04:48 +08:00
40b3686f2e chore(deps): update dependency @playwright/test to v1.57.0
Some checks failed
CI/CD Pipeline / build-and-deploy (push) Has been cancelled
CI/CD Pipeline / playwright (push) Has been cancelled
2025-12-11 21:03:49 +08:00
dfc64ecece 更新 .github/workflows/ci-cd.yaml
Some checks failed
CI/CD Pipeline / playwright (push) Failing after 2m0s
CI/CD Pipeline / build-and-deploy (push) Has been skipped
2025-12-11 20:58:32 +08:00
1df5110034 chore(deps): update dependency @prettier/plugin-oxc to ^0.1.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m36s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m55s
2025-12-11 17:44:41 +08:00
731a3da55d chore(deps): update dependency @vueuse/core to v14.1.0
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m39s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m48s
2025-12-11 15:08:52 +08:00
4e5b3cb96c chore(deps): update dependency vite to v7.2.7
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m44s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m56s
2025-12-11 11:47:59 +08:00
5e8df2572f chore(deps): update dependency @cloudflare/vite-plugin to v1.15.3
Some checks failed
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 2m5s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m47s
测试最新依赖 / build-and-test (push) Successful in 1m41s
测试最新依赖 / playwright (push) Failing after 2m12s
2025-12-11 01:01:20 +08:00
严浩
7d6474c92b refactor: update loadPlugin to use async/await and improve vueRouter configuration
All checks were successful
CI/CD Pipeline / playwright (push) Successful in 1m44s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m40s
2025-12-11 00:05:04 +08:00
严浩
86920da611 refactor: update component names to PascalCase and improve structure
All checks were successful
CI/CD Pipeline / playwright (push) Successful in 2m17s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m50s
- Changed component names from kebab-case to PascalCase in various files for consistency.
- Updated `<router-view>` and `<transition>` to `<RouterView>` and `<Transition>` respectively in App.vue and base-layout.vue.
- Refactored AppNaiveUIProvider.vue to use PascalCase for Naive UI providers.
- Adjusted language and theme switch buttons to use PascalCase for icon components.
- Updated button components in demo pages to use PascalCase for Naive UI buttons.
- Modified ESLint rules in route message files to use perfectionist/sort-objects for better key sorting.
- Enhanced Vite plugin files to export loadPlugin functions for better plugin management.
- Improved plugin loading logic to handle errors and warnings more effectively.
2025-12-10 22:52:23 +08:00
3828f12a2d chore(deps): update dependency vue-i18n to v11.2.2
All checks were successful
renovate/stability-days Updates have met minimum release age requirement
CI/CD Pipeline / playwright (push) Successful in 1m50s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m55s
2025-12-10 14:06:40 +08:00
58 changed files with 2361 additions and 1843 deletions

View File

@@ -1,65 +1,31 @@
{ {
"image": "ghcr.io/yanhao98/h-devcontainer:main", "image": "ghcr.io/yanhao98/h-devcontainer:main",
"runArgs": ["--name=${localWorkspaceFolderBasename}-devcontainer"], "runArgs": [
"forwardPorts": [4730, 4731], // vscode://settings/remote.localPortHost -> 默认只监听 localhost "--hostname=devcontainer-host",
"--name=${localWorkspaceFolderBasename}-devcontainer"
],
"forwardPorts": [4730, 4731, 5901],
"portsAttributes": { "portsAttributes": {
"4730": { "label": "开发服务器端口", "onAutoForward": "notify" }, "4730": { "label": "开发服务器端口", "onAutoForward": "notify" },
"4731": { "label": "预览服务器端口", "onAutoForward": "notify" } "4731": { "label": "预览服务器端口", "onAutoForward": "notify" }
}, },
"remoteEnv": {
"ANTHROPIC_AUTH_TOKEN": "${localEnv:ANTHROPIC_AUTH_TOKEN}",
"ANTHROPIC_BASE_URL": "${localEnv:ANTHROPIC_BASE_URL}",
"GEMINI_API_KEY": "${localEnv:GEMINI_API_KEY}",
"GOOGLE_GEMINI_BASE_URL": "${localEnv:GOOGLE_GEMINI_BASE_URL}"
},
"containerEnv": { "containerEnv": {
"NODE_OPTIONS": "--max-old-space-size=4096",
"TZ": "${localEnv:TZ:Asia/Shanghai}" "TZ": "${localEnv:TZ:Asia/Shanghai}"
}, },
"customizations": { "customizations": {
"vscode": { "vscode": {
"extensions": [ "extensions": ["prettier.prettier-vscode", "vue.volar"],
// AI
"github.copilot-chat",
"anthropic.claude-code",
"google.gemini-cli-vscode-ide-companion",
"vicanent.gcmp",
// >>>>>
// "eamodio.gitlens",
"tu6ge.naive-ui-intelligence",
"gruntfuggly.todo-tree",
"lokalise.i18n-ally",
"vitest.explorer",
"antfu.unocss",
"vue.volar",
// <<<<<
// 代码质量 / 格式化 / Lint
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint",
"oxc.oxc-vscode",
"esbenp.prettier-vscode"
],
"settings": { "settings": {
"editor.formatOnSave": true, // "tasks": { "version": "2.0.0", "tasks": [] },
"editor.defaultFormatter": "esbenp.prettier-vscode", "github.copilot.chat.codeGeneration.instructions": [
"editor.codeActionsOnSave": { {
"source.fixAll.eslint": "explicit" "text": "This dev container includes a lightweight Fluxbox based desktop that can be accessed using a VNC viewer or the web. GUI-based commands executed from the built-in VS Code terminal will open on the desktop automatically."
}, }
"chat.extensionUnification.enabled": true, ]
"chat.tools.terminal.autoApprove": {
"/.*/": true,
"git push": false
},
// * 尽管使用了“/.*/”,但有些还是会失败,因为有几个错误的默认值:
// * https://github.com/microsoft/vscode/issues/266651#issuecomment-3292581459
"chat.tools.terminal.ignoreDefaultAutoApproveRules": true,
"tasks": { "version": "2.0.0", "tasks": [] },
"terminal.integrated.defaultProfile.linux": "zsh"
} }
} }
}, },
"mounts": [ "mounts": [
// 不挂载还可能会遇到:`Cannot run macOS (Mach-O) executable in Docker: Exec format error`
"source=${localWorkspaceFolderBasename}-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume", "source=${localWorkspaceFolderBasename}-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume",
"source=${localWorkspaceFolder}/.devcontainer/lifecycle-scripts.d,target=/usr/local/etc/lifecycle-scripts.d,type=bind,consistency=delegated" "source=${localWorkspaceFolder}/.devcontainer/lifecycle-scripts.d,target=/usr/local/etc/lifecycle-scripts.d,type=bind,consistency=delegated"
], ],
@@ -69,7 +35,5 @@
"updateContentCommand": "/usr/local/bin/run-lifecycle-scripts.sh updateContentCommand", "updateContentCommand": "/usr/local/bin/run-lifecycle-scripts.sh updateContentCommand",
"postCreateCommand": "/usr/local/bin/run-lifecycle-scripts.sh postCreateCommand", "postCreateCommand": "/usr/local/bin/run-lifecycle-scripts.sh postCreateCommand",
"postStartCommand": "/usr/local/bin/run-lifecycle-scripts.sh postStartCommand", "postStartCommand": "/usr/local/bin/run-lifecycle-scripts.sh postStartCommand",
"postAttachCommand": "/usr/local/bin/run-lifecycle-scripts.sh postAttachCommand", "postAttachCommand": "/usr/local/bin/run-lifecycle-scripts.sh postAttachCommand"
"waitFor": "updateContentCommand",
"remoteUser": "usr_vscode"
} }

View File

@@ -1,13 +0,0 @@
#!/bin/zsh -eu
# 打印带有颜色的欢迎信息
echo -e "\033[1;32m↘ 容器首次创建!\033[0m"
# 修复权限问题(比如 node_modules 目录)
sudo chown -R $(whoami):$(whoami) /workspaces || true
# 调用内置命令 (这些命令在 _build-context/builtin-scripts 目录中的脚本中定义)
h-setup-locale
h-setup-zsh
h-setup-bun-bin
h-setup-pnpm-bin

View File

@@ -1,13 +0,0 @@
#!/bin/bash -eu
# 容器内容更新时的消息和依赖安装
echo '↘️ 容器内容已更新!'
# 安装依赖
if command -v pnpm >/dev/null 2>&1; then
# 跳过: The modules directory at "/workspaces/h-devcontainers/node_modules" will be removed and reinstalled from scratch. Proceed? (Y/n) ·
time pnpm install --config.confirmModulesPurge=false
else
echo '❌错误: pnpm 未安装'
exit 0
fi

View File

@@ -0,0 +1,17 @@
#!/bin/zsh -eu
h-setup-zh-locale
h-setup-bun-bin
h-setup-pnpm-bin
h-setup-ai-claude-code --config
h-setup-ai-gemini-cli --config
h-setup-chromium
h-setup-desktop-lite
h-install-node-modules
echo "-----------------------------"
echo "开发容器已启动并配置完成!"
echo "-----------------------------"

View File

@@ -14,7 +14,7 @@ on:
jobs: jobs:
playwright: playwright:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: mcr.microsoft.com/playwright:v1.56.1-noble container: mcr.microsoft.com/playwright:v1.57.0-noble
steps: steps:
- name: ⚙️ 设置 Node 环境 - name: ⚙️ 设置 Node 环境
uses: yanhao98/composite-actions/setup-node-environment@faab20ac2f9c85dfce1a4147fca493bf632bd744 uses: yanhao98/composite-actions/setup-node-environment@faab20ac2f9c85dfce1a4147fca493bf632bd744

View File

@@ -60,7 +60,7 @@ jobs:
playwright: playwright:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: mcr.microsoft.com/playwright:v1.56.1-noble container: mcr.microsoft.com/playwright:v1.57.0-noble
steps: steps:
- uses: actions/checkout@main - uses: actions/checkout@main
with: with:

3
.ignore Normal file
View File

@@ -0,0 +1,3 @@
# VS Code 搜索忽略
pnpm-lock.yaml

View File

@@ -4,5 +4,6 @@
"tabWidth": 2, "tabWidth": 2,
"singleQuote": true, "singleQuote": true,
"printWidth": 100, "printWidth": 100,
"trailingComma": "all" "trailingComma": "all",
"plugins": ["@prettier/plugin-oxc"]
} }

View File

@@ -5,7 +5,7 @@
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"EditorConfig.EditorConfig", "EditorConfig.EditorConfig",
"oxc.oxc-vscode", "oxc.oxc-vscode",
"esbenp.prettier-vscode", "prettier.prettier-vscode",
"stylelint.vscode-stylelint", "stylelint.vscode-stylelint",
"lokalise.i18n-ally" "lokalise.i18n-ally"
] ]

50
.vscode/settings.json vendored
View File

@@ -1,29 +1,54 @@
{ {
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.preferences.autoImportFileExcludePatterns": [
"vue-router$",
"**/src/composables/**",
"**/*-auto-imports.ts",
"**/*-auto-imports.types.ts"
],
"search.exclude": {
"public/report-ui-dist": true
},
// ============================================================
// 代码格式化相关配置
// ============================================================
"prettier.enable": true,
"files.readonlyInclude": {
"dist/**": true
},
"eslint.enable": true,
"oxc.enable": true,
"stylelint.enable": true,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll": "explicit", "source.fixAll": "explicit",
"source.fixAll.eslint": "explicit", "source.fixAll.eslint": "explicit",
"source.fixAll.stylelint": "explicit",
"source.fixAll.oxc": "explicit", "source.fixAll.oxc": "explicit",
"source.organizeImports": "never" "source.fixAll.stylelint": "explicit",
"source.organizeImports": "explicit"
}, },
"eslint.enable": true,
"stylelint.enable": true,
"oxc.enable": true,
"editor.formatOnSave": true,
"stylelint.validate": ["css", "less", "postcss", "scss", "vue"], "stylelint.validate": ["css", "less", "postcss", "scss", "vue"],
"scss.lint.unknownAtRules": "ignore", "scss.lint.unknownAtRules": "ignore",
"css.lint.unknownAtRules": "ignore", "css.lint.unknownAtRules": "ignore",
"less.lint.unknownAtRules": "ignore", "less.lint.unknownAtRules": "ignore",
"editor.defaultFormatter": "prettier.prettier-vscode",
"[typescript]": {
"editor.defaultFormatter": "prettier.prettier-vscode"
},
"[vue]": { "[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "prettier.prettier-vscode"
}, },
"[json]": { "[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "prettier.prettier-vscode"
}, },
"[jsonc]": { "[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "prettier.prettier-vscode"
}, },
"editor.defaultFormatter": "esbenp.prettier-vscode",
// ============================================================
// i18n-ally 配置
// ============================================================
// >>>>> // >>>>>
"i18n-ally.readonly": false, "i18n-ally.readonly": false,
"i18n-ally.namespace": false /* @intlify/unplugin-vue-i18n */, "i18n-ally.namespace": false /* @intlify/unplugin-vue-i18n */,
@@ -34,10 +59,7 @@
"i18n-ally.enabledParsers": ["json"], "i18n-ally.enabledParsers": ["json"],
"i18n-ally.keystyle": "nested", "i18n-ally.keystyle": "nested",
"i18n-ally.sourceLanguage": "zh-CN", // 翻译源语言 (源文件) 根据此语言文件翻译其他语言文件的变量和内容 "i18n-ally.sourceLanguage": "zh-CN", // 翻译源语言 (源文件) 根据此语言文件翻译其他语言文件的变量和内容
"i18n-ally.displayLanguage": "zh-CN", // 显示语言 (显示文件/翻译文件) "i18n-ally.displayLanguage": "zh-CN" // 显示语言 (显示文件/翻译文件)
// <<<<< // <<<<<
// https://github.com/copilot/share/8a1a019a-0180-80e7-8141-a40be02c4006
// "iconify.customCollectionJsonPaths": ["https://example.com/my-icons.json", "./local/icons.json"], // "iconify.customCollectionJsonPaths": ["https://example.com/my-icons.json", "./local/icons.json"],
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.preferences.autoImportFileExcludePatterns": ["vue-router/auto$"]
} }

6
auto-imports.d.ts vendored
View File

@@ -42,6 +42,7 @@ declare global {
const debouncedWatch: typeof import('@vueuse/core').debouncedWatch const debouncedWatch: typeof import('@vueuse/core').debouncedWatch
const deepFreeze: typeof import('deep-freeze-es6').default const deepFreeze: typeof import('deep-freeze-es6').default
const defineAsyncComponent: typeof import('vue').defineAsyncComponent const defineAsyncComponent: typeof import('vue').defineAsyncComponent
const defineBasicLoader: typeof import('unplugin-vue-router/data-loaders/basic').defineBasicLoader
const defineComponent: typeof import('vue').defineComponent const defineComponent: typeof import('vue').defineComponent
const defineStore: typeof import('pinia').defineStore const defineStore: typeof import('pinia').defineStore
const eagerComputed: typeof import('@vueuse/core').eagerComputed const eagerComputed: typeof import('@vueuse/core').eagerComputed
@@ -64,7 +65,6 @@ declare global {
const isRef: typeof import('vue').isRef const isRef: typeof import('vue').isRef
const isShallow: typeof import('vue').isShallow const isShallow: typeof import('vue').isShallow
const makeDestructurable: typeof import('@vueuse/core').makeDestructurable const makeDestructurable: typeof import('@vueuse/core').makeDestructurable
const manualResetRef: typeof import('@vueuse/core').manualResetRef
const mapActions: typeof import('pinia').mapActions const mapActions: typeof import('pinia').mapActions
const mapGetters: typeof import('pinia').mapGetters const mapGetters: typeof import('pinia').mapGetters
const mapState: typeof import('pinia').mapState const mapState: typeof import('pinia').mapState
@@ -222,7 +222,6 @@ declare global {
const useIntervalFn: typeof import('@vueuse/core').useIntervalFn const useIntervalFn: typeof import('@vueuse/core').useIntervalFn
const useKeyModifier: typeof import('@vueuse/core').useKeyModifier const useKeyModifier: typeof import('@vueuse/core').useKeyModifier
const useLastChanged: typeof import('@vueuse/core').useLastChanged const useLastChanged: typeof import('@vueuse/core').useLastChanged
const useLink: typeof import('vue-router/auto').useLink
const useLoadingBar: typeof import('naive-ui').useLoadingBar const useLoadingBar: typeof import('naive-ui').useLoadingBar
const useLocalStorage: typeof import('@vueuse/core').useLocalStorage const useLocalStorage: typeof import('@vueuse/core').useLocalStorage
const useMagicKeys: typeof import('@vueuse/core').useMagicKeys const useMagicKeys: typeof import('@vueuse/core').useMagicKeys
@@ -388,6 +387,7 @@ declare module 'vue' {
readonly debouncedWatch: UnwrapRef<typeof import('@vueuse/core')['debouncedWatch']> readonly debouncedWatch: UnwrapRef<typeof import('@vueuse/core')['debouncedWatch']>
readonly deepFreeze: UnwrapRef<typeof import('deep-freeze-es6')['default']> readonly deepFreeze: UnwrapRef<typeof import('deep-freeze-es6')['default']>
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']> readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
readonly defineBasicLoader: UnwrapRef<typeof import('unplugin-vue-router/data-loaders/basic')['defineBasicLoader']>
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']> readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
readonly defineStore: UnwrapRef<typeof import('pinia')['defineStore']> readonly defineStore: UnwrapRef<typeof import('pinia')['defineStore']>
readonly eagerComputed: UnwrapRef<typeof import('@vueuse/core')['eagerComputed']> readonly eagerComputed: UnwrapRef<typeof import('@vueuse/core')['eagerComputed']>
@@ -410,7 +410,6 @@ declare module 'vue' {
readonly isRef: UnwrapRef<typeof import('vue')['isRef']> readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
readonly isShallow: UnwrapRef<typeof import('vue')['isShallow']> readonly isShallow: UnwrapRef<typeof import('vue')['isShallow']>
readonly makeDestructurable: UnwrapRef<typeof import('@vueuse/core')['makeDestructurable']> readonly makeDestructurable: UnwrapRef<typeof import('@vueuse/core')['makeDestructurable']>
readonly manualResetRef: UnwrapRef<typeof import('@vueuse/core')['manualResetRef']>
readonly mapActions: UnwrapRef<typeof import('pinia')['mapActions']> readonly mapActions: UnwrapRef<typeof import('pinia')['mapActions']>
readonly mapGetters: UnwrapRef<typeof import('pinia')['mapGetters']> readonly mapGetters: UnwrapRef<typeof import('pinia')['mapGetters']>
readonly mapState: UnwrapRef<typeof import('pinia')['mapState']> readonly mapState: UnwrapRef<typeof import('pinia')['mapState']>
@@ -568,7 +567,6 @@ declare module 'vue' {
readonly useIntervalFn: UnwrapRef<typeof import('@vueuse/core')['useIntervalFn']> readonly useIntervalFn: UnwrapRef<typeof import('@vueuse/core')['useIntervalFn']>
readonly useKeyModifier: UnwrapRef<typeof import('@vueuse/core')['useKeyModifier']> readonly useKeyModifier: UnwrapRef<typeof import('@vueuse/core')['useKeyModifier']>
readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']> readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']>
readonly useLink: UnwrapRef<typeof import('vue-router/auto')['useLink']>
readonly useLoadingBar: UnwrapRef<typeof import('naive-ui')['useLoadingBar']> readonly useLoadingBar: UnwrapRef<typeof import('naive-ui')['useLoadingBar']>
readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']> readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']>
readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']> readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']>

View File

@@ -1,9 +1,11 @@
import type { UserConfig } from '@commitlint/types'; import type { UserConfig } from '@commitlint/types';
const Configuration: UserConfig = { const Configuration: UserConfig = {
extends: ['@commitlint/config-conventional'], extends: ['@commitlint/config-conventional'],
formatter: '@commitlint/format', formatter: '@commitlint/format',
rules: {
'body-max-line-length': [2, 'always', 500],
},
}; };
export default Configuration; export default Configuration;

View File

@@ -10,6 +10,7 @@ import {
import pluginImport from 'eslint-plugin-import'; import pluginImport from 'eslint-plugin-import';
import pluginJsonc from 'eslint-plugin-jsonc'; import pluginJsonc from 'eslint-plugin-jsonc';
import pluginOxlint from 'eslint-plugin-oxlint'; import pluginOxlint from 'eslint-plugin-oxlint';
import pluginPerfectionist from 'eslint-plugin-perfectionist';
import pluginPlaywright from 'eslint-plugin-playwright'; import pluginPlaywright from 'eslint-plugin-playwright';
import pluginVue from 'eslint-plugin-vue'; import pluginVue from 'eslint-plugin-vue';
import { globalIgnores } from 'eslint/config'; import { globalIgnores } from 'eslint/config';
@@ -24,7 +25,14 @@ export default defineConfigWithVueTs(
files: ['**/*.{ts,mts,tsx,vue}'], files: ['**/*.{ts,mts,tsx,vue}'],
}, },
globalIgnores(['worker-configuration.d.ts', '**/dist/**', '**/dist-ssr/**', '**/coverage/**']), globalIgnores([
'worker-configuration.d.ts',
'**/dist/**',
'**/dist-ssr/**',
'**/coverage/**',
'**/public/**',
'**/-----TEMP-----/**',
]),
pluginVue.configs['flat/essential'], pluginVue.configs['flat/essential'],
vueTsConfigs.recommended, vueTsConfigs.recommended,
@@ -45,6 +53,13 @@ export default defineConfigWithVueTs(
{ {
rules: { rules: {
'@intlify/vue-i18n/no-raw-text': 'off', '@intlify/vue-i18n/no-raw-text': 'off',
'@intlify/vue-i18n/no-unused-keys': [
'error',
{
src: './src',
extensions: ['.js', '.ts', '.tsx', '.vue'],
},
],
}, },
settings: { settings: {
'vue-i18n': { 'vue-i18n': {
@@ -54,9 +69,7 @@ export default defineConfigWithVueTs(
}, },
}, },
{ {
plugins: { plugins: { import: pluginImport },
import: pluginImport,
},
rules: { rules: {
'import/first': 'error', 'import/first': 'error',
'import/no-duplicates': 'error', 'import/no-duplicates': 'error',
@@ -70,6 +83,8 @@ export default defineConfigWithVueTs(
}, },
}, },
{ plugins: { perfectionist: pluginPerfectionist } },
{ {
/** /**
* 启用 sort-keys 规则以强制对象键按字母顺序排序 * 启用 sort-keys 规则以强制对象键按字母顺序排序
@@ -102,7 +117,11 @@ export default defineConfigWithVueTs(
'vue/attributes-order': 'error', 'vue/attributes-order': 'error',
'vue/multi-word-component-names': 'off', 'vue/multi-word-component-names': 'off',
'vue/padding-line-between-blocks': ['error', 'always'], 'vue/padding-line-between-blocks': ['error', 'always'],
'vue/component-name-in-template-casing': [
'error',
'PascalCase',
{ registeredComponentsOnly: false, ignores: [] },
],
// '@stylistic/padding-line-between-statements': [ // '@stylistic/padding-line-between-statements': [
// 'error', // 'error',
// { blankLine: 'always', prev: '*', next: ['const', 'let', 'var'] }, // { blankLine: 'always', prev: '*', next: ['const', 'let', 'var'] },

View File

@@ -1,5 +1,5 @@
{ {
"packageManager": "pnpm@10.23.0", "packageManager": "pnpm@10.25.0",
"name": "vue-ts-example-2025", "name": "vue-ts-example-2025",
"version": "0.0.0", "version": "0.0.0",
"private": true, "private": true,
@@ -14,7 +14,7 @@
}, },
"scripts": { "scripts": {
"all": "run-s lint format:prettier build-only type-check test:unit:DisableWatch", "all": "run-s lint format:prettier build-only type-check test:unit:DisableWatch",
"dev": "vite --port 4730 --host --strictPort", "dev": "nodemon --delay 280ms --watch vite-plugins --ext ts -x vite -- --port 4730 --host --strictPort",
"build": "run-p type-check \"build-only {@}\" --", "build": "run-p type-check \"build-only {@}\" --",
"build-only": "vite build", "build-only": "vite build",
"preview": "vite preview --port 4731 --host --strictPort", "preview": "vite preview --port 4731 --host --strictPort",
@@ -57,8 +57,8 @@
"@commitlint/cli": "^20.1.0", "@commitlint/cli": "^20.1.0",
"@commitlint/config-conventional": "^20.0.0", "@commitlint/config-conventional": "^20.0.0",
"@formkit/auto-animate": "^0.9.0", "@formkit/auto-animate": "^0.9.0",
"@pinia/colada": "^0.17.8", "@pinia/colada": "^0.19.0",
"@primeuix/themes": "^1.2.5", "@primeuix/themes": "^2.0.0",
"@sa/materials": "workspace:*", "@sa/materials": "workspace:*",
"@unhead/vue": "^2.0.19", "@unhead/vue": "^2.0.19",
"@vueuse/core": "^14.0.0", "@vueuse/core": "^14.0.0",
@@ -71,7 +71,7 @@
"primelocale": "^2.2.2", "primelocale": "^2.2.2",
"primevue": "^4.4.1", "primevue": "^4.4.1",
"ts-enum-util": "^4.1.0", "ts-enum-util": "^4.1.0",
"utils4u": "^4.2.3", "utils4u": "^5",
"vue": "^3.5.24", "vue": "^3.5.24",
"vue-i18n": "^11.2.1", "vue-i18n": "^11.2.1",
"vue-memoize-dict": "^1.1.3", "vue-memoize-dict": "^1.1.3",
@@ -86,8 +86,8 @@
"@iconify-json/material-symbols": "^1.2.47", "@iconify-json/material-symbols": "^1.2.47",
"@intlify/eslint-plugin-vue-i18n": "^4.1.0", "@intlify/eslint-plugin-vue-i18n": "^4.1.0",
"@intlify/unplugin-vue-i18n": "^11.0.1", "@intlify/unplugin-vue-i18n": "^11.0.1",
"@playwright/test": "^1.56.1", "@playwright/test": "^1.57.0",
"@prettier/plugin-oxc": "^0.0.5", "@prettier/plugin-oxc": "^0.1.3",
"@primevue/auto-import-resolver": "^4.4.1", "@primevue/auto-import-resolver": "^4.4.1",
"@primevue/metadata": "^4.4.1", "@primevue/metadata": "^4.4.1",
"@stylelint-types/stylelint-order": "^7.0.0", "@stylelint-types/stylelint-order": "^7.0.0",
@@ -106,11 +106,13 @@
"@vue/eslint-config-typescript": "^14.6.0", "@vue/eslint-config-typescript": "^14.6.0",
"@vue/test-utils": "^2.4.6", "@vue/test-utils": "^2.4.6",
"@vue/tsconfig": "^0.8.1", "@vue/tsconfig": "^0.8.1",
"boxen": "^8.0.1",
"consola": "^3.4.2", "consola": "^3.4.2",
"eslint": "^9.39.1", "eslint": "^9.39.1",
"eslint-plugin-import": "^2.32.0", "eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsonc": "^2.21.0", "eslint-plugin-jsonc": "^2.21.0",
"eslint-plugin-oxlint": "~1.29.0", "eslint-plugin-oxlint": "~1.32.0",
"eslint-plugin-perfectionist": "^5.0.0",
"eslint-plugin-playwright": "^2.3.0", "eslint-plugin-playwright": "^2.3.0",
"eslint-plugin-vue": "~10.6.0", "eslint-plugin-vue": "~10.6.0",
"happy-dom": "^20.0.10", "happy-dom": "^20.0.10",
@@ -118,11 +120,12 @@
"husky": "^9.1.7", "husky": "^9.1.7",
"jsdom": "^27.2.0", "jsdom": "^27.2.0",
"lint-staged": "^16.2.7", "lint-staged": "^16.2.7",
"nodemon": "^3.1.11",
"npm-run-all2": "^8.0.4", "npm-run-all2": "^8.0.4",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"oxlint": "~1.29.0", "oxlint": "~1.29.0",
"postcss-html": "^1.8.0", "postcss-html": "^1.8.0",
"prettier": "3.6.2", "prettier": "3.7.4",
"rollup": "^4.53.3", "rollup": "^4.53.3",
"sass-embedded": "^1.93.3", "sass-embedded": "^1.93.3",
"sharp": "^0.34.5", "sharp": "^0.34.5",
@@ -144,7 +147,7 @@
"unplugin-vue-markdown": "^29.2.0", "unplugin-vue-markdown": "^29.2.0",
"unplugin-vue-router": "^0.19.0", "unplugin-vue-router": "^0.19.0",
"vite": "^7.2.4", "vite": "^7.2.4",
"vite-plugin-checker": "^0.11.0", "vite-plugin-checker": "^0.12.0",
"vite-plugin-fake-server": "^2.2.2", "vite-plugin-fake-server": "^2.2.2",
"vite-plugin-image-optimizer": "^2.0.3", "vite-plugin-image-optimizer": "^2.0.3",
"vite-plugin-vue-devtools": "^8.0.5", "vite-plugin-vue-devtools": "^8.0.5",
@@ -154,13 +157,7 @@
"vue-component-type-helpers": "^3.1.4", "vue-component-type-helpers": "^3.1.4",
"vue-i18n-extract": "^2.0.7", "vue-i18n-extract": "^2.0.7",
"vue-macros": "3.1.1", "vue-macros": "3.1.1",
"vue-tsc": "^3.1.4", "vue-tsc": "^3.1.8",
"wrangler": "^4.50.0" "wrangler": "^4.50.0"
}, }
"overrides": {
"vue-tsc": "$vue-tsc"
},
"workspaces": [
"packages/*"
]
} }

3103
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,6 @@
packages: packages:
- 'packages/*' - 'packages/*'
# shamefullyHoist: false # https://pnpm.io/zh/settings#shamefullyhoist # shamefullyHoist: false # https://pnpm.io/zh/settings#shamefullyhoist
overrides:
vue-tsc: $vue-tsc

View File

@@ -9,10 +9,10 @@ import AppNaiveUIProvider from './AppNaiveUIProvider.vue';
<Toast style="z-index: 5000" /> <Toast style="z-index: 5000" />
<AppNaiveUIProvider> <AppNaiveUIProvider>
<router-view v-slot="{ Component }"> <RouterView v-slot="{ Component }">
<transition name="fade" mode="out-in"> <Transition name="fade" mode="out-in">
<component :is="Component" /> <component :is="Component" />
</transition> </Transition>
</router-view> </RouterView>
</AppNaiveUIProvider> </AppNaiveUIProvider>
</template> </template>

View File

@@ -42,17 +42,17 @@ declare global {
:theme="appStore.isDark ? darkTheme : null" :theme="appStore.isDark ? darkTheme : null"
abstract abstract
> >
<n-loading-bar-provider> <NLoadingBarProvider>
<n-message-provider> <NMessageProvider>
<n-notification-provider> <NNotificationProvider>
<n-modal-provider> <NModalProvider>
<n-dialog-provider> <NDialogProvider>
<slot></slot> <slot></slot>
<ContextHolder /> <ContextHolder />
</n-dialog-provider> </NDialogProvider>
</n-modal-provider> </NModalProvider>
</n-notification-provider> </NNotificationProvider>
</n-message-provider> </NMessageProvider>
</n-loading-bar-provider> </NLoadingBarProvider>
</NConfigProvider> </NConfigProvider>
</template> </template>

View File

@@ -1,6 +1,5 @@
import { createGetRoutes } from '@/plugins/00.router-plugin';
import type { MenuInst, MenuOption } from 'naive-ui'; import type { MenuInst, MenuOption } from 'naive-ui';
import { createGetRoutes } from 'virtual:meta-layouts';
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import type { RouteRecordRaw } from 'vue-router'; import type { RouteRecordRaw } from 'vue-router';
import { RouterLink } from 'vue-router'; import { RouterLink } from 'vue-router';

View File

@@ -25,7 +25,7 @@ function handleSelect(key: string) {
<NDropdown trigger="hover" placement="bottom-end" :options="options" @select="handleSelect"> <NDropdown trigger="hover" placement="bottom-end" :options="options" @select="handleSelect">
<NButton quaternary class="flex items-center gap-1"> <NButton quaternary class="flex items-center gap-1">
<template #icon> <template #icon>
<icon-clarity-language-line w-4.5 h-4.5 /> <IconClarityLanguageLine w-4.5 h-4.5 />
</template> </template>
<span>{{ languageLabels[locale] }}</span> <span>{{ languageLabels[locale] }}</span>
</NButton> </NButton>

View File

@@ -13,17 +13,17 @@ const themeLabels: Record<AppThemeMode, string> = {
{{ themeLabels[appStore.themeMode] }} {{ themeLabels[appStore.themeMode] }}
<template #trigger> <template #trigger>
<NButton quaternary @click="appStore.cycleTheme()"> <NButton quaternary @click="appStore.cycleTheme()">
<icon-line-md-sunny-filled-loop-to-moon-filled-loop-transition <IconLineMdSunnyFilledLoopToMoonFilledLoopTransition
v-if="appStore.themeMode === 'light'" v-if="appStore.themeMode === 'light'"
w-4.5 w-4.5
h-4.5 h-4.5
/> />
<icon-line-md-moon-filled-to-sunny-filled-loop-transition <IconLineMdMoonFilledToSunnyFilledLoopTransition
v-else-if="appStore.themeMode === 'dark'" v-else-if="appStore.themeMode === 'dark'"
w-4.5 w-4.5
h-4.5 h-4.5
/> />
<icon-line-md-computer v-else w-4.5 h-4.5 /> <IconLineMdComputer v-else w-4.5 h-4.5 />
</NButton> </NButton>
</template> </template>
</NTooltip> </NTooltip>

View File

@@ -15,8 +15,8 @@ function toggleCollapsed() {
{{ appStore.sidebarCollapsed ? '展开菜单' : '收起菜单' }} {{ appStore.sidebarCollapsed ? '展开菜单' : '收起菜单' }}
<template #trigger> <template #trigger>
<NButton ref="buttonRef" quaternary @click="toggleCollapsed"> <NButton ref="buttonRef" quaternary @click="toggleCollapsed">
<icon-line-md-menu-fold-right v-if="appStore.sidebarCollapsed" w-4.5 h-4.5 /> <IconLineMdMenuFoldRight v-if="appStore.sidebarCollapsed" w-4.5 h-4.5 />
<icon-line-md-menu-fold-left v-else w-4.5 h-4.5 /> <IconLineMdMenuFoldLeft v-else w-4.5 h-4.5 />
</NButton> </NButton>
</template> </template>
</NTooltip> </NTooltip>

View File

@@ -39,7 +39,7 @@ function handleSelect(key: string) {
<template> <template>
<NDropdown :options="options" placement="bottom-end" @select="handleSelect"> <NDropdown :options="options" placement="bottom-end" @select="handleSelect">
<NButton quaternary circle> <NButton quaternary circle>
<icon-material-symbols-account-circle w-5 h-5 /> <IconMaterialSymbolsAccountCircle w-5 h-5 />
</NButton> </NButton>
</NDropdown> </NDropdown>
</template> </template>

View File

@@ -29,11 +29,11 @@ const appStore = useAppStore();
<BaseLayoutSider /> <BaseLayoutSider />
</template> </template>
<!-- <div>GlobalContent</div> --> <!-- <div>GlobalContent</div> -->
<router-view v-slot="{ Component }"> <RouterView v-slot="{ Component }">
<transition name="fade" mode="out-in"> <Transition name="fade" mode="out-in">
<component :is="Component" /> <component :is="Component" />
</transition> </Transition>
</router-view> </RouterView>
<!-- <div>ThemeDrawer</div> --> <!-- <div>ThemeDrawer</div> -->
<template #footer> <template #footer>
<div <div

View File

@@ -4,7 +4,8 @@
*/ */
import messages from '@intlify/unplugin-vue-i18n/messages'; import messages from '@intlify/unplugin-vue-i18n/messages';
import { createGetRoutes, router } from '@/plugins/00.router-plugin'; import { router } from '@/plugins/00.router-plugin';
import { createGetRoutes } from 'virtual:meta-layouts';
import { createI18n } from 'vue-i18n'; import { createI18n } from 'vue-i18n';
import { START_LOCATION } from 'vue-router'; import { START_LOCATION } from 'vue-router';

View File

@@ -1,9 +1,12 @@
/*eslint sort-keys: "error"*/ /*eslint perfectionist/sort-objects: "error"*/
/** /**
* 启用 sort-keys 规则以强制对象键按字母顺序排序 * 启用 perfectionist/sort-objects 规则以强制对象键按字母顺序排序
* 原因: * 原因:
* 1. 减少多人协作时的合并冲突 * 1. 减少多人协作时的合并冲突
* 2. 保持代码一致性,提高可维护性 * 2. 保持代码一致性,提高可维护性
*
* 运行以下命令自动修复排序:
* pnpm exec eslint --fix --no-ignore src/locales-utils/route-messages/
*/ */
export default { export default {

View File

@@ -1,9 +1,12 @@
/*eslint sort-keys: "error"*/ /*eslint perfectionist/sort-objects: "error"*/
/** /**
* 启用 sort-keys 规则以强制对象键按字母顺序排序 * 启用 perfectionist/sort-objects 规则以强制对象键按字母顺序排序
* 原因: * 原因:
* 1. 减少多人协作时的合并冲突 * 1. 减少多人协作时的合并冲突
* 2. 保持代码一致性,提高可维护性 * 2. 保持代码一致性,提高可维护性
*
* 运行以下命令自动修复排序:
* pnpm exec eslint --fix --no-ignore src/locales-utils/route-messages/
*/ */
export default { export default {

View File

@@ -1,15 +0,0 @@
<script setup lang="ts">
definePage({
// name: false,
meta: {
_file: '(layout-group).page.vue',
},
});
</script>
<template>
<div>
<div> b:</div>
<RouterView />
</div>
</template>

View File

@@ -1,12 +0,0 @@
<script setup lang="ts">
definePage({
alias: '/',
meta: {
_file: '(layout)/a.page.vue',
},
});
</script>
<template>
<div>a.page.vue</div>
</template>

View File

@@ -1,11 +0,0 @@
<script setup lang="ts">
definePage({
meta: {
_file: '(layout)/b.page.vue',
},
});
</script>
<template>
<div>b.page.vue</div>
</template>

View File

@@ -1,11 +0,0 @@
<script setup lang="ts">
definePage({
meta: {
_file: '(ccc)/c.page.vue',
},
});
</script>
<template>
<div>c.page.vue</div>
</template>

View File

@@ -1,13 +0,0 @@
<script setup lang="ts">
definePage({
meta: {
_file: '(home).page.vue',
},
});
</script>
<template>
<div>
<n-button @click="$router.push({ name: 'DemosCreate' })">DemosCreate</n-button>
</div>
</template>

View File

@@ -1,7 +0,0 @@
<script setup lang="ts">
import TheBaseLayout from '@/layouts/base-layout/the-base-layout.vue';
</script>
<template>
<TheBaseLayout></TheBaseLayout>
</template>

View File

@@ -128,7 +128,7 @@ const resetCount = () => {
</button> </button>
<!-- Naive UI 按钮 --> <!-- Naive UI 按钮 -->
<n-button <NButton
type="warning" type="warning"
size="large" size="large"
block block
@@ -148,7 +148,7 @@ const resetCount = () => {
</svg> </svg>
</template> </template>
点击 +1 (Naive UI) 点击 +1 (Naive UI)
</n-button> </NButton>
<!-- 重置按钮 --> <!-- 重置按钮 -->
<button <button

View File

@@ -8,5 +8,5 @@ definePage({
</script> </script>
<template> <template>
<div>create.page.vue</div> <div></div>
</template> </template>

View File

@@ -9,29 +9,29 @@ function setLocale(newLocale: 'en-US' | 'zh-CN') {
<template> <template>
<div class="p-4"> <div class="p-4">
<n-h1>{{ t('page.i18n-demo.title') }}</n-h1> <NH1>{{ t('page.i18n-demo.title') }}</NH1>
<n-card :title="t('page.i18n-demo.change-language')"> <NCard :title="t('page.i18n-demo.change-language')">
<n-p> <NP>
{{ t('page.i18n-demo.current-language') }}: {{ t('page.i18n-demo.current-language') }}:
<span class="font-bold">{{ locale }}</span> <span class="font-bold">{{ locale }}</span>
</n-p> </NP>
<n-p> <NP>
{{ t('page.i18n-demo.hello', { name: 'Kilo' }) }} {{ t('page.i18n-demo.hello', { name: 'Kilo' }) }}
</n-p> </NP>
<n-space> <NSpace>
<n-button type="primary" @click="setLocale('en-US')"> English </n-button> <NButton type="primary" @click="setLocale('en-US')"> English </NButton>
<n-button type="success" @click="setLocale('zh-CN')"> 简体中文 </n-button> <NButton type="success" @click="setLocale('zh-CN')"> 简体中文 </NButton>
</n-space> </NSpace>
</n-card> </NCard>
<!-- 这里响应式有问题: --> <!-- 这里响应式有问题: -->
<n-p> $route.meta.title: {{ $route.meta.title }} </n-p> <NP> $route.meta.title: {{ $route.meta.title }} </NP>
<!-- 这样才正常 --> <!-- 这样才正常 -->
<n-p> <NP>
routeI18nInstance.global.t($route.name): {{ routeI18nInstance.global.t($route.name) }} routeI18nInstance.global.t($route.name): {{ routeI18nInstance.global.t($route.name) }}
</n-p> </NP>
</div> </div>
</template> </template>

7
src/pages/index.page.vue Normal file
View File

@@ -0,0 +1,7 @@
<script setup lang="ts"></script>
<template>
<div>
<NButton @click="$router.push({ name: 'DemosCreate' })">DemosCreate</NButton>
</div>
</template>

View File

@@ -1,13 +1,14 @@
import { DataLoaderPlugin } from 'unplugin-vue-router/data-loaders'; import { DataLoaderPlugin } from 'unplugin-vue-router/data-loaders';
// import { setupLayouts } from 'virtual:meta-layouts'; import { setupLayouts } from 'virtual:meta-layouts';
// import { createGetRoutes, setupLayouts } from 'virtual:generated-layouts';
import { createRouter, createWebHistory } from 'vue-router'; import { createRouter, createWebHistory } from 'vue-router';
import type { Router } from 'vue-router'; import type { Router } from 'vue-router';
import { handleHotUpdate, routes } from 'vue-router/auto-routes'; import { handleHotUpdate, routes } from 'vue-router/auto-routes';
// const setupLayoutsResult = setupLayouts(routes); const setupLayoutsResult = setupLayouts(routes);
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
routes: routes /* ?? setupLayoutsResult */, routes: /* routes ?? */ setupLayoutsResult,
scrollBehavior: (_to, _from, savedPosition) => { scrollBehavior: (_to, _from, savedPosition) => {
return savedPosition ?? { left: 0, top: 0 }; return savedPosition ?? { left: 0, top: 0 };
}, },
@@ -49,11 +50,6 @@ export function install({ app }: { app: import('vue').App<Element> }) {
// <<< // <<<
} }
export function createGetRoutes(router: Router) {
const routes = router.getRoutes();
return () => routes.filter((route) => !route.meta.isLayout);
}
if (__DEV__) Object.assign(window, { router }); if (__DEV__) Object.assign(window, { router });
// This will update routes at runtime without reloading the page // This will update routes at runtime without reloading the page
@@ -61,4 +57,4 @@ if (import.meta.hot) {
handleHotUpdate(router); handleHotUpdate(router);
} }
export { router }; export { router, setupLayoutsResult };

View File

@@ -20,6 +20,7 @@
}, },
"vueCompilerOptions": { "vueCompilerOptions": {
"plugins": [ "plugins": [
"vue-macros/volar",
"unplugin-vue-router/volar/sfc-route-blocks", "unplugin-vue-router/volar/sfc-route-blocks",
"unplugin-vue-router/volar/sfc-typed-router" "unplugin-vue-router/volar/sfc-typed-router"
] ]

72
typed-router.d.ts vendored
View File

@@ -28,28 +28,6 @@ declare module 'vue-router/auto-routes' {
'/', '/',
Record<never, never>, Record<never, never>,
Record<never, never>, Record<never, never>,
| 'RootA'
| 'RootB'
>,
'RootA': RouteRecordInfo<
'RootA',
'/a',
Record<never, never>,
Record<never, never>,
| never
>,
'RootB': RouteRecordInfo<
'RootB',
'/b',
Record<never, never>,
Record<never, never>,
| never
>,
'RootC': RouteRecordInfo<
'RootC',
'/c',
Record<never, never>,
Record<never, never>,
| never | never
>, >,
'$Path': RouteRecordInfo< '$Path': RouteRecordInfo<
@@ -59,20 +37,6 @@ declare module 'vue-router/auto-routes' {
{ path: ParamValue<false> }, { path: ParamValue<false> },
| never | never
>, >,
'DemosParent': RouteRecordInfo<
'DemosParent',
'/demos',
Record<never, never>,
Record<never, never>,
| 'Demos'
| 'DemosApiDemo'
| 'DemosCounterDemo'
| 'DemosCreate'
| 'DemosI18nDemo'
| 'DemosNaiveUiDemo'
| 'DemosPrimevueDemo'
| 'DemosWebsocketDemo'
>,
'Demos': RouteRecordInfo< 'Demos': RouteRecordInfo<
'Demos', 'Demos',
'/demos', '/demos',
@@ -156,29 +120,9 @@ declare module 'vue-router/auto-routes' {
* @internal * @internal
*/ */
export interface _RouteFileInfoMap { export interface _RouteFileInfoMap {
'src/pages-with-layout/(layout-group).page.vue': { 'src/pages/index.page.vue': {
routes: routes:
| 'Root' | 'Root'
| 'RootA'
| 'RootB'
views:
| 'default'
}
'src/pages-with-layout/(layout-group)/a.page.vue': {
routes:
| 'RootA'
views:
| never
}
'src/pages-with-layout/(layout-group)/b.page.vue': {
routes:
| 'RootB'
views:
| never
}
'src/pages/(ccc)/c.page.vue': {
routes:
| 'RootC'
views: views:
| never | never
} }
@@ -188,20 +132,6 @@ declare module 'vue-router/auto-routes' {
views: views:
| never | never
} }
'src/pages/demos.page.vue': {
routes:
| 'Demos'
| 'DemosApiDemo'
| 'DemosCounterDemo'
| 'DemosCreate'
| 'DemosI18nDemo'
| 'DemosNaiveUiDemo'
| 'DemosParent'
| 'DemosPrimevueDemo'
| 'DemosWebsocketDemo'
views:
| 'default'
}
'src/pages/demos/index.page.vue': { 'src/pages/demos/index.page.vue': {
routes: routes:
| 'Demos' | 'Demos'

View File

@@ -1,32 +1,33 @@
import vue from '@vitejs/plugin-vue'; import vue from '@vitejs/plugin-vue';
import vueJsx from '@vitejs/plugin-vue-jsx'; import vueJsx from '@vitejs/plugin-vue-jsx';
import consola from 'consola';
import { getPascalCaseRouteName } from 'unplugin-vue-router';
import vueRouter from 'unplugin-vue-router/vite'; import vueRouter from 'unplugin-vue-router/vite';
import type { PluginOption } from 'vite';
import VueMacros from 'vue-macros/vite'; import VueMacros from 'vue-macros/vite';
export default [ import type { LoadPluginFunction } from './_loadPlugins';
VueMacros({
plugins: {
vue: vue({ include: [/\.vue$/, /\.md$/] }),
vueJsx: vueJsx(),
// https://uvr.esm.is/guide/configuration.html export const loadPlugin: LoadPluginFunction = async (_pluginLoadOptions) => {
// https://github.com/posva/unplugin-vue-router return [
// ⚠️ Vue must be placed after VueRouter() VueMacros({
vueRouter: vueRouter({ plugins: {
exclude: ['**/__*', '**/__*/**/*'], vue: vue({ include: [/\.vue$/, /\.md$/] }),
extensions: ['.page.vue', '.page.md'], vueJsx: vueJsx(),
getRouteName: (routeNode) => getPascalCaseRouteName(routeNode),
logs: true, // https://uvr.esm.is/guide/configuration.html
routesFolder: ['src/pages', 'src/pages-with-layout'], // https://github.com/posva/unplugin-vue-router
extendRoute(route) { // ⚠️ Vue must be placed after VueRouter()
consola.info(`route.name :>> `, route.name); vueRouter: vueRouter({
console.debug(`route.meta :>> `, route.meta); routesFolder: 'src/pages',
console.debug(`route.path :>> `, route.path); extensions: ['.page.vue', '.page.md'],
}, exclude: ['**/__*', '**/__*/**/*'],
}), getRouteName: (await import('unplugin-vue-router')).getPascalCaseRouteName,
}, beforeWriteFiles(rootRoute) {
}), for (/* 深度优先遍历 */ const route of rootRoute.traverseDFS()) {
] satisfies PluginOption; route.addToMeta({ _: route.fullPath });
}
},
logs: !true,
}),
},
}),
];
};

View File

@@ -1,10 +1,13 @@
import type { PluginOption } from 'vite';
import UnoCSS from 'unocss/vite'; import UnoCSS from 'unocss/vite';
export default [ import type { LoadPluginFunction } from './_loadPlugins';
// https://github.com/antfu/unocss
// see uno.config.ts for config export const loadPlugin: LoadPluginFunction = (_pluginLoadOptions) => {
UnoCSS({ return [
checkImport: true, // https://github.com/antfu/unocss
}), // see uno.config.ts for config
] satisfies PluginOption; UnoCSS({
checkImport: true,
}),
];
};

View File

@@ -1,18 +1,21 @@
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'; import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
import type { PluginOption } from 'vite';
export default [ import type { LoadPluginFunction } from './_loadPlugins';
// https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n
VueI18nPlugin({
/* options */
// locale messages resource pre-compile option
include: ['src/locales/**'],
// https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n#transformi18nblock export const loadPlugin: LoadPluginFunction = (_pluginLoadOptions) => {
// transformI18nBlock(src) { return [
// console.debug(`src :>> `, src); // https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n
// console.debug(`typeof src :>> `, typeof src); VueI18nPlugin({
// return src as string; /* options */
// }, // locale messages resource pre-compile option
}), include: ['src/locales/**'],
] satisfies PluginOption;
// https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n#transformi18nblock
// transformI18nBlock(src) {
// console.debug(`src :>> `, src);
// console.debug(`typeof src :>> `, typeof src);
// return src as string;
// },
}),
];
};

View File

@@ -1,9 +1,12 @@
import type { PluginOption } from 'vite';
import Markdown from 'unplugin-vue-markdown/vite'; import Markdown from 'unplugin-vue-markdown/vite';
export default [ import type { LoadPluginFunction } from './_loadPlugins';
// https://github.com/unplugin/unplugin-vue-markdown
Markdown({ export const loadPlugin: LoadPluginFunction = (_pluginLoadOptions) => {
headEnabled: true, return [
}), // https://github.com/unplugin/unplugin-vue-markdown
] satisfies PluginOption; Markdown({
headEnabled: true,
}),
];
};

View File

@@ -1,17 +0,0 @@
import type { PluginOption } from 'vite';
import MetaLayouts from 'vite-plugin-vue-meta-layouts';
export default [
// https://github.com/dishait/vite-plugin-vue-meta-layouts
MetaLayouts({
target: 'src/layouts',
excludes: ['**/!(the-)*.vue'], // 排除非 the- 开头的文件。
metaName: 'layout',
// defaultLayout: 'sakai-vue/AppLayout',
// defaultLayout: 'naive-ui/AppLayout',
defaultLayout: 'base-layout/the-base-layout',
// !⬇️: 当设置为 `sync` 时,注意`import 'virtual:uno.css'`的顺序问题。
// importMode: 'sync', // 默认为自动处理SSG 时为 sync非 SSG 时为 async
skipTopLevelRouteLayout: true, // 打开修复 https://github.com/JohnCampionJr/vite-plugin-vue-layouts/issues/134默认为 false 关闭
}),
] satisfies PluginOption;

View File

@@ -0,0 +1,20 @@
import MetaLayouts from 'vite-plugin-vue-meta-layouts';
import type { LoadPluginFunction } from './_loadPlugins';
export const loadPlugin: LoadPluginFunction = (_pluginLoadOptions) => {
return [
// https://github.com/dishait/vite-plugin-vue-meta-layouts
MetaLayouts({
target: 'src/layouts',
excludes: ['**/!(the-)*.vue'], // 排除非 the- 开头的文件。
metaName: 'layout',
// defaultLayout: 'sakai-vue/AppLayout',
// defaultLayout: 'naive-ui/AppLayout',
defaultLayout: 'base-layout/the-base-layout',
// !⬇️: 当设置为 `sync` 时,注意`import 'virtual:uno.css'`的顺序问题。
// importMode: 'sync', // 默认为自动处理SSG 时为 sync非 SSG 时为 async
skipTopLevelRouteLayout: true, // 打开修复 https://github.com/JohnCampionJr/vite-plugin-vue-layouts/issues/134默认为 false 关闭
}),
];
};

View File

@@ -9,7 +9,6 @@ import Icons from 'unplugin-icons/vite';
import Components from 'unplugin-vue-components/vite'; import Components from 'unplugin-vue-components/vite';
import { VueRouterAutoImports } from 'unplugin-vue-router'; import { VueRouterAutoImports } from 'unplugin-vue-router';
import { createUtils4uAutoImports } from 'utils4u/auto-imports'; import { createUtils4uAutoImports } from 'utils4u/auto-imports';
import type { ConfigEnv, PluginOption } from 'vite';
// >>>>> // >>>>>
// eslint-disable-next-line import/no-duplicates // eslint-disable-next-line import/no-duplicates
@@ -26,6 +25,8 @@ import { PrimeVueResolver } from '@primevue/auto-import-resolver';
import { VantResolver } from '@vant/auto-import-resolver'; import { VantResolver } from '@vant/auto-import-resolver';
// <<<<< // <<<<<
import type { LoadPluginFunction } from './_loadPlugins';
function _getNaiveUiComponentNames() { function _getNaiveUiComponentNames() {
// [dtsTsx](https://github.com/unplugin/unplugin-vue-components/pull/673/files/84e80738885cfe11298f41f070cda94a7a779276) // [dtsTsx](https://github.com/unplugin/unplugin-vue-components/pull/673/files/84e80738885cfe11298f41f070cda94a7a779276)
@@ -55,7 +56,7 @@ function _getNaiveUiComponentNames() {
return []; return [];
} }
export function loadPlugin(_configEnv: ConfigEnv): PluginOption { export const loadPlugin: LoadPluginFunction = (_pluginLoadOptions) => {
return [ return [
// https://github.com/antfu/unplugin-auto-import // https://github.com/antfu/unplugin-auto-import
AutoImport({ AutoImport({
@@ -76,7 +77,7 @@ export function loadPlugin(_configEnv: ConfigEnv): PluginOption {
createUtils4uAutoImports(['primevue']), createUtils4uAutoImports(['primevue']),
{ {
'consola/browser': ['consola'], 'consola/browser': ['consola'],
'vue-router/auto': ['useLink'], 'unplugin-vue-router/data-loaders/basic': ['defineBasicLoader'],
'naive-ui': [ 'naive-ui': [
'useModal', 'useModal',
'useDialog', 'useDialog',
@@ -141,4 +142,4 @@ export function loadPlugin(_configEnv: ConfigEnv): PluginOption {
}, },
}), }),
]; ];
} };

View File

@@ -1,6 +1,7 @@
import { minify as minifyHtml } from 'html-minifier-terser'; import { minify as minifyHtml } from 'html-minifier-terser';
import { loadEnv } from 'vite'; import type { PluginOption } from 'vite';
import type { ConfigEnv, PluginOption } from 'vite';
import type { LoadPluginFunction } from './_loadPlugins';
function IndexHtmlPlugin(): PluginOption { function IndexHtmlPlugin(): PluginOption {
return { return {
@@ -91,8 +92,14 @@ function ___(): PluginOption {
}; };
} }
export function loadPlugin(_configEnv: ConfigEnv): PluginOption { export const loadPlugin: LoadPluginFunction = (pluginLoadOptions) => {
const { mode, env } = pluginLoadOptions;
// return [___()]; // return [___()];
const env = loadEnv(_configEnv.mode, process.cwd()); if (mode !== 'production') {
if (env.VITE_BUILD_MINIFY === 'true') return IndexHtmlPlugin(); return { plugins: [], message: '仅在生产模式下启用' };
} }
if (env.VITE_BUILD_MINIFY !== 'true') {
return { plugins: [], message: `已通过环境变量禁用: VITE_BUILD_MINIFY=${env.VITE_BUILD_MINIFY}` };
}
return IndexHtmlPlugin();
};

View File

@@ -1,16 +1,16 @@
import { consola } from 'consola';
import type { ConfigEnv, PluginOption } from 'vite';
import { vitePluginFakeServer } from 'vite-plugin-fake-server'; import { vitePluginFakeServer } from 'vite-plugin-fake-server';
// https://github.com/condorheroblog/vite-plugin-fake-server?tab=readme-ov-file#usage
export function loadPlugin(_configEnv: ConfigEnv): PluginOption { import type { LoadPluginFunction } from './_loadPlugins';
if (_configEnv.mode !== 'development') {
consola.info('fake server plugin is disabled in non-development mode.'); // https://github.com/condorheroblog/vite-plugin-fake-server?tab=readme-ov-file#usage
return []; export const loadPlugin: LoadPluginFunction = (pluginLoadOptions) => {
const { mode } = pluginLoadOptions;
if (mode !== 'development') {
return { plugins: [], message: '仅在开发模式下启用' };
} }
return vitePluginFakeServer({ return vitePluginFakeServer({
basename: 'fake-api', basename: 'fake-api',
enableProd: true, enableProd: true,
include: 'fake', include: 'fake',
}); });
} };

View File

@@ -1,9 +1,12 @@
import type { PluginOption } from 'vite';
import { ViteImageOptimizer } from 'vite-plugin-image-optimizer'; import { ViteImageOptimizer } from 'vite-plugin-image-optimizer';
export default [ import type { LoadPluginFunction } from './_loadPlugins';
// https://github.com/FatehAK/vite-plugin-image-optimizer?tab=readme-ov-file#default-configuration
ViteImageOptimizer({ export const loadPlugin: LoadPluginFunction = (_pluginLoadOptions) => {
/* pass your config */ return [
}), // https://github.com/FatehAK/vite-plugin-image-optimizer?tab=readme-ov-file#default-configuration
] satisfies PluginOption; ViteImageOptimizer({
/* pass your config */
}),
];
};

View File

@@ -1,23 +1,27 @@
import consola from 'consola';
import type { ConfigEnv, PluginOption } from 'vite';
import vueDevTools from 'vite-plugin-vue-devtools'; import vueDevTools from 'vite-plugin-vue-devtools';
export function loadPlugin(configEnv: ConfigEnv): PluginOption { import type { LoadPluginFunction } from './_loadPlugins';
if (configEnv.mode !== 'development') {
consola.info('vue-devtools 插件仅在开发模式下使用。'); export const loadPlugin: LoadPluginFunction = (_pluginLoadOptions) => {
return []; const { mode } = _pluginLoadOptions;
if (mode !== 'development') {
return { plugins: [], message: '仅在开发模式下启用' };
} }
let launchEditor = 'code'; let launchEditor = 'code';
let message: string | undefined;
if (process.env.TERM_PROGRAM_VERSION?.toLowerCase()?.includes('insider')) { if (process.env.TERM_PROGRAM_VERSION?.toLowerCase()?.includes('insider')) {
consola.info('检测到 VSCode Insiders 环境。');
launchEditor = 'code-insiders'; launchEditor = 'code-insiders';
message = '检测到 VSCode Insiders 环境';
} }
return [ return {
vueDevTools({ plugins: [
launchEditor: launchEditor, vueDevTools({
}), launchEditor: launchEditor,
]; }),
} ],
message,
};
};

View File

@@ -1,16 +1,17 @@
import { cloudflare } from '@cloudflare/vite-plugin'; import { cloudflare } from '@cloudflare/vite-plugin';
import { loadEnv } from 'vite';
import type {ConfigEnv, PluginOption} from 'vite';
export function loadPlugin(_configEnv: ConfigEnv): PluginOption { import type { LoadPluginFunction } from './_loadPlugins';
const env = loadEnv(_configEnv.mode, process.cwd());
if (_configEnv.mode === 'test') { export const loadPlugin: LoadPluginFunction = (pluginLoadOptions) => {
console.log('cloudflare plugin disabled in test mode'); const { mode, env } = pluginLoadOptions;
return []; if (mode === 'test') {
return { plugins: [], message: '在测试模式下禁用' };
} }
if (env.VITE_CLOUDFLARE_SERVER_ENABLED !== 'true') { if (env.VITE_CLOUDFLARE_SERVER_ENABLED !== 'true') {
console.log('cloudflare plugin disabled by env'); return {
return []; plugins: [],
message: `已通过环境变量禁用: VITE_CLOUDFLARE_SERVER_ENABLED=${env.VITE_CLOUDFLARE_SERVER_ENABLED}`,
};
} }
return [cloudflare()]; return [cloudflare()];
} };

View File

@@ -1,30 +1,47 @@
import boxen from 'boxen';
import consola from 'consola';
import path from 'node:path'; import path from 'node:path';
import { pathToFileURL } from 'node:url'; import { pathToFileURL } from 'node:url';
import consola from 'consola';
import { glob } from 'tinyglobby'; import { glob } from 'tinyglobby';
import type { ConfigEnv, PluginOption } from 'vite'; import type { ConfigEnv, PluginOption } from 'vite';
import { loadEnv } from 'vite';
export type LoadPluginFunction = (
configEnv: ConfigEnv & {
env: Record<string, string>;
},
) => PluginOption | LoadPluginResult;
export interface LoadPluginResult {
plugins: PluginOption;
message?: string;
}
type LoadPluginFunction = (configEnv: ConfigEnv) => PluginOption;
export async function loadPlugins(configEnv: ConfigEnv): Promise<PluginOption[]> { export async function loadPlugins(configEnv: ConfigEnv): Promise<PluginOption[]> {
const plugins: PluginOption[] = []; const plugins: PluginOption[] = [];
consola.start('开始加载 Vite 插件...'); const cwd = path.resolve(import.meta.dirname);
const pluginEntries = await glob('**/*.ts', { const pluginEntries = await glob('**/*.ts', {
absolute: true, absolute: true,
cwd: path.resolve(import.meta.dirname), cwd,
ignore: [ ignore: [
'**/*.d.ts', '**/*.d.ts',
'**/*.disabled.ts', '**/*.disabled.ts',
'**/*.x.ts',
'**/*.X.ts',
'**/x-*.ts', // 禁用以 x- 开头的插件文件 '**/x-*.ts', // 禁用以 x- 开头的插件文件
'**/*-x.ts', // 禁用以 -x 结尾的插件文件
'**/*-X.ts', // 禁用以 -X 结尾的插件文件
'**/_*', '**/_*',
], ],
}); });
consola.info(`找到 ${pluginEntries.length} 个插件文件`); const relativeCwd = path.relative(process.cwd(), cwd);
console.time('加载插件');
consola.log(
boxen(`正在加载 Vite 插件... (./${relativeCwd})`, {
borderStyle: 'classic',
borderColor: 'cyan',
}),
);
// 计算最长的文件名长度,用于对齐输出 // 计算最长的文件名长度,用于对齐输出
const maxNameLength = Math.max(...pluginEntries.map((entry) => path.basename(entry).length)); const maxNameLength = Math.max(...pluginEntries.map((entry) => path.basename(entry).length));
@@ -35,36 +52,39 @@ export async function loadPlugins(configEnv: ConfigEnv): Promise<PluginOption[]>
const imported = await import(pathToFileURL(entry).href); const imported = await import(pathToFileURL(entry).href);
const loadPlugin = imported.loadPlugin as LoadPluginFunction | undefined; const loadPlugin = imported.loadPlugin as LoadPluginFunction | undefined;
let plugin: PluginOption | undefined;
let loadMethod = '';
// 优先使用 loadPlugin 函数(接收 configEnv 参数) if (!loadPlugin || typeof loadPlugin !== 'function') {
if (loadPlugin && typeof loadPlugin === 'function') { consola.warn(`插件未导出 loadPlugin 函数: ${paddedName}`);
plugin = loadPlugin(configEnv); continue;
loadMethod = 'loadPlugin';
} else if (imported.default) {
plugin = imported.default;
loadMethod = 'default';
} else {
consola.warn(`插件未导出有效内容: ${paddedName}`);
continue; // 跳过无效插件
} }
if (plugin) { const env = loadEnv(configEnv.mode, process.cwd());
const pluginArray = Array.isArray(plugin) ? plugin : [plugin]; const result = loadPlugin({ ...configEnv, env });
const validPlugins = pluginArray.filter(Boolean); // 过滤掉 null/undefined
const pluginCount = validPlugins.length;
if (pluginCount > 0) { // 判断是否是 LoadPluginResult 对象
plugins.push(...validPlugins); const isResultObject = (val: unknown): val is LoadPluginResult =>
consola.success(`${paddedName}${pluginCount} 个实例 (${loadMethod})`); typeof val === 'object' && val !== null && 'plugins' in val;
} else {
consola.info(`${paddedName} 返回了空数组或无效值`); const plugin = isResultObject(result) ? result.plugins : result;
} const message = isResultObject(result) ? result.message : undefined;
const pluginArray = Array.isArray(plugin) ? plugin : [plugin];
const validPlugins = pluginArray.filter(Boolean);
const pluginCount = validPlugins.length;
if (pluginCount > 0) {
plugins.push(...validPlugins);
const suffix = message ? ` (${message})` : '';
consola.info(`${paddedName}${pluginCount} 个实例${suffix}`);
} else if (message) {
consola.info(`${paddedName}${message}`);
} else {
consola.info(`${paddedName} 返回了空数组或无效值`);
} }
} }
consola.success(`✅ 总共加载了 ${plugins.length} 个插件实例`); consola.success(` ${pluginEntries.length} 个插件文件,已加载 ${plugins.length}实例`);
console.timeEnd('加载插件');
return plugins; return plugins;
} }

View File

@@ -1,13 +1,16 @@
import type { ConfigEnv, PluginOption } from 'vite'; import type { LoadPluginFunction } from './_loadPlugins';
import { loadEnv } from 'vite';
export default [ export const loadPlugin: LoadPluginFunction = (_pluginLoadOptions) => {
// ... const env = _pluginLoadOptions.env;
] satisfies PluginOption;
export function loadPlugin(_configEnv: ConfigEnv): PluginOption { // 示例:根据环境变量禁用插件并返回消息
const env = loadEnv(_configEnv.mode, process.cwd()); if (env.VITE_DEMO_ENABLED !== 'true') {
console.debug(`env :>> `, env); return {
// ... plugins: [],
return undefined; message: `已通过环境变量禁用: VITE_DEMO_ENABLED=${env.VITE_DEMO_ENABLED}`,
} };
}
// 正常返回插件
return [];
};

View File

@@ -1,17 +0,0 @@
import type { PluginOption } from 'vite';
export default [
// // 检查是否在VS Code终端中运行
// if (process.env.TERM_PROGRAM === 'vscode' || process.env.VSCODE_PID) {
// // plugins.push(
// // // 构建后自动将dist目录打包成zip文件
// // viteArchiverPlugin({
// // addTimestamp: false, // 是否添加时间戳到输出文件名
// // format: 'zip', // 输出的压缩文件格式
// // outputDir: '', // 输出目录,默认为项目根目录
// // outputFileName: 'dist', // 输出的zip文件名不含扩展名
// // sourceDir: 'dist', // 要打包的源目录
// // }),
// // )
// }
] satisfies PluginOption;

View File

@@ -1,6 +1,6 @@
import type { ManualChunkMeta, PreRenderedAsset, RollupOptions } from 'rollup'; import type { /* ManualChunkMeta, PreRenderedAsset, */ RollupOptions } from 'rollup';
import path from 'node:path'; // import path from 'node:path';
// https://www.npmjs.com/package/utils4u/v/2.19.2?activeTab=code // https://www.npmjs.com/package/utils4u/v/2.19.2?activeTab=code
@@ -12,106 +12,95 @@ export const viteConfigRollupOptions: RollupOptions = {
if (warning.code === 'EVAL' && warning.id?.includes('node_modules/protobufjs')) return; if (warning.code === 'EVAL' && warning.id?.includes('node_modules/protobufjs')) return;
warn(warning); warn(warning);
}, */ }, */
output: { output: {
// Keep hashed file names predictable across entry, chunk, and asset outputs. // 如果一个 chunk 小于 10KBRollup 会尝试将它合并到其他 chunk 中。这样可以避免产生大量碎片文件
entryFileNames: 'entry/[name].[hash].js', // 默认: "[name].js" experimentalMinChunkSize: 10 * 1024,
chunkFileNames: 'chunk/[name].[hash].js', // 默认: "[name]-[hash].js" // // Keep hashed file names predictable across entry, chunk, and asset outputs.
// assetFileNames:'', // 默认: "assets/[name]-[hash][extname]" // entryFileNames: 'entry/[name].[hash].js', // 默认: "[name].js"
// https://cn.rollupjs.org/configuration-options/#output-assetfilenames // chunkFileNames: 'chunk/[name].[hash].js', // 默认: "[name]-[hash].js"
assetFileNames(chunkInfo: PreRenderedAsset) { // // assetFileNames:'', // 默认: "assets/[name]-[hash][extname]"
const names = [...new Set(chunkInfo.names)]; // // https://cn.rollupjs.org/configuration-options/#output-assetfilenames
// assetFileNames(chunkInfo: PreRenderedAsset) {
if (names.length !== 1) { // const names = [...new Set(chunkInfo.names)];
console.error('Multiple names for asset:', chunkInfo); // if (names.length !== 1) {
process.exit(1); // console.error('Multiple names for asset:', chunkInfo);
} // process.exit(1);
// }
const assetName = names[0]; // const assetName = names[0];
const ext = assetName.split('.').pop()?.toLowerCase(); // const ext = assetName.split('.').pop()?.toLowerCase();
if (ext && /png|jpe?g|gif|svg|webp|avif/.test(ext)) { // if (ext && /png|jpe?g|gif|svg|webp|avif/.test(ext)) {
return 'chunks/images/[name].[hash][extname]'; // return 'chunks/images/[name].[hash][extname]';
} // }
if (ext && /woff2?|ttf|otf/.test(ext)) { // if (ext && /woff2?|ttf|otf/.test(ext)) {
return 'chunks/fonts/[name].[hash][extname]'; // return 'chunks/fonts/[name].[hash][extname]';
} // }
if (ext === 'css') { // if (ext === 'css') {
return 'chunks/css/[name].[hash][extname]'; // return 'chunks/css/[name].[hash][extname]';
} // }
return '_chunks/[name].[hash][extname]'; // return '_chunks/[name].[hash][extname]';
}, // },
// manualChunks: (id: string, _meta: ManualChunkMeta) => {
manualChunks: (id: string, _meta: ManualChunkMeta) => { // // https://github.com/unocss/unocss/issues/4917
// https://github.com/unocss/unocss/issues/4917 // // if (['/src/layouts'].some((prefix) => id.includes(prefix))) {
// if (['/src/layouts'].some((prefix) => id.includes(prefix))) { // // const url = new URL(id, 'file://');
// const url = new URL(id, 'file://'); // // if (!url.search /* ?vue&type=script&setup=true&lang.ts */) {
// if (!url.search /* ?vue&type=script&setup=true&lang.ts */) { // // return 'layouts';
// return 'layouts'; // // }
// } // // }
// } // if (id.includes('meta-layouts')) {
// // console.debug(`id :>> `, id); // id :>> virtual:meta-layouts
if (id.includes('meta-layouts')) { // // 这里很奇怪,打印 id 是`virtual:meta-layouts`,但是 `'virtual:meta-layouts' === id` 却是 false
// console.debug(`id :>> `, id); // id :>> virtual:meta-layouts // return 'lib-meta-layouts';
// 这里很奇怪,打印 id 是`virtual:meta-layouts`,但是 `'virtual:meta-layouts' === id` 却是 false // }
return 'lib-meta-layouts'; // if (id.includes('index.page.vue')) {
} // const url = new URL(id, 'file://');
// if (!url.search /* ?vue&type=script&setup=true&lang.ts */) {
if (id.includes('index.page.vue')) { // const parentDir = path.basename(path.dirname(id));
const url = new URL(id, 'file://'); // return `${parentDir}-index.page`;
if (!url.search /* ?vue&type=script&setup=true&lang.ts */) { // }
const parentDir = path.basename(path.dirname(id)); // }
return `${parentDir}-index.page`; // if (!id.includes('node_modules')) return;
} // // 处理 pnpm 的特殊路径结构
} // let packageName;
// if (id.includes('.pnpm')) {
if (!id.includes('node_modules')) return; // // pnpm 路径: .pnpm/naive-ui@2.43.1_vue@3.5.22/node_modules/naive-ui/...
// 处理 pnpm 的特殊路径结构 // const pnpmMatch = id.match(/\.pnpm\/(.+?)@/);
let packageName; // if (pnpmMatch) {
if (id.includes('.pnpm')) { // packageName = pnpmMatch[1];
// pnpm 路径: .pnpm/naive-ui@2.43.1_vue@3.5.22/node_modules/naive-ui/... // }
const pnpmMatch = id.match(/\.pnpm\/(.+?)@/); // } else {
if (pnpmMatch) { // // 普通路径: node_modules/lodash/...
packageName = pnpmMatch[1]; // const normalMatch = id.match(/node_modules\/(@[^/]+\/[^/]+|[^/]+)\//);
} // if (normalMatch) {
} else { // packageName = normalMatch[1];
// 普通路径: node_modules/lodash/... // }
const normalMatch = id.match(/node_modules\/(@[^/]+\/[^/]+|[^/]+)\//); // }
if (normalMatch) { // if (packageName) {
packageName = normalMatch[1]; // if (['highlight.js'].includes(packageName)) {
} // return 'lib-hljs';
} // }
// // 根据包名分组
if (packageName) { // if (['consola', 'lodash', '@juggle+resize-observer', 'vueuc'].includes(packageName)) {
if (['highlight.js'].includes(packageName)) { // return 'lib-vendor';
return 'lib-hljs'; // }
} // // // 拆了有问题
// // if (['naive-ui'].includes(packageName) && id.includes('_internal')) {
// 根据包名分组 // // return 'lib-naive-ui-internal';
if (['consola', 'lodash', '@juggle+resize-observer', 'vueuc'].includes(packageName)) { // // }
return 'lib-vendor'; // if (['naive-ui'].includes(packageName)) {
} // return 'lib-naive-ui';
// }
// // 拆了有问题 // if (
// if (['naive-ui'].includes(packageName) && id.includes('_internal')) { // ['primelocale', 'primevue', 'primeuix', 'primeicons'].some((name) =>
// return 'lib-naive-ui-internal'; // packageName!.includes(name),
// } // )
// ) {
if (['naive-ui'].includes(packageName)) { // return 'lib-primevue';
return 'lib-naive-ui'; // }
} // if (['vue', 'vue-router', 'pinia', 'vue-demi', 'vue-i18n'].includes(packageName)) {
// return 'lib-vue-vendor';
if ( // }
['primelocale', 'primevue', 'primeuix', 'primeicons'].some((name) => // }
packageName!.includes(name), // },
)
) {
return 'lib-primevue';
}
if (['vue', 'vue-router', 'pinia', 'vue-demi', 'vue-i18n'].includes(packageName)) {
return 'lib-vue-vendor';
}
}
},
}, },
}; };

View File

@@ -3,7 +3,7 @@ import { createViteProxy } from 'utils4u/vite';
import { defineConfig, loadEnv } from 'vite'; import { defineConfig, loadEnv } from 'vite';
import { loadPlugins } from './vite-plugins/_loadPlugins'; import { loadPlugins } from './vite-plugins/_loadPlugins';
import { optimizeDeps } from './vite.config.optimizeDeps'; import { optimizeDeps } from './vite.config.optimizeDeps';
// import { viteConfigRollupOptions } from './vite.config.rollup'; import { viteConfigRollupOptions } from './vite.config.rollup';
import consola from 'consola'; import consola from 'consola';
// https://vite.dev/config/ // https://vite.dev/config/
@@ -24,7 +24,7 @@ export default defineConfig(async (configEnv) => {
build: { build: {
minify: env.VITE_BUILD_MINIFY === 'true' ? undefined /* 即默认 */ : false, // 默认: 'terser' minify: env.VITE_BUILD_MINIFY === 'true' ? undefined /* 即默认 */ : false, // 默认: 'terser'
sourcemap: env.VITE_BUILD_SOURCE_MAP === 'true', sourcemap: env.VITE_BUILD_SOURCE_MAP === 'true',
// rollupOptions: viteConfigRollupOptions, rollupOptions: viteConfigRollupOptions,
}, },
css: { css: {
devSourcemap: true, devSourcemap: true,