From 943401b0fdfa4b27a11a7f88fb9ae9040ec430b2 Mon Sep 17 00:00:00 2001 From: "Ray.Hao" <1490493387@qq.com> Date: Fri, 16 Oct 2026 16:51:00 +0800 Subject: [PATCH] =?UTF-8?q?chore(check):=20=E6=96=B0=E5=A2=9E=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E7=B1=BB=E5=9E=8B=E4=B8=8E=E6=96=87=E4=BB=B6=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E6=A3=80=E6=9F=A5=E5=B9=B6=E6=8E=A5=E5=85=A5=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=E9=93=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 模板类型检查用独立 tsconfig 只统计 src 下错误 - 新增自有 Vue 文件 PascalCase 检查脚本 - check 命令串联类型检查、模板检查、命名检查与 lint --- package.json | 3 +- scripts/check-naming.mjs | 53 +++++++++++++++++++++++++++++++++++ scripts/check-templates.mjs | 55 +++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 scripts/check-naming.mjs create mode 100644 scripts/check-templates.mjs diff --git a/package.json b/package.json index 43a25f5..0407b60 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,8 @@ "build:quickapp-webview-union": "uni build -p quickapp-webview-union", "type-check": "vue-tsc --noEmit", "type-check:templates": "node scripts/check-templates.mjs", - "check": "pnpm type-check && pnpm type-check:templates && pnpm lint:eslint:check && pnpm lint:stylelint:check", + "check:naming": "node scripts/check-naming.mjs", + "check": "pnpm type-check && pnpm type-check:templates && pnpm check:naming && pnpm lint:eslint:check && pnpm lint:stylelint:check", "lint:eslint:check": "eslint \"src/**/*.{vue,ts}\"", "lint:eslint": "eslint \"src/**/*.{vue,ts}\" --fix", "lint:prettier": "prettier --write \"**/*.{js,cjs,ts,json,css,scss,vue,html,md}\"", diff --git a/scripts/check-naming.mjs b/scripts/check-naming.mjs new file mode 100644 index 0000000..df89211 --- /dev/null +++ b/scripts/check-naming.mjs @@ -0,0 +1,53 @@ +import { readdirSync } from "node:fs"; +import { join, relative, sep } from "node:path"; + +/** + * 文件命名检查 + * + * 自有 Vue 文件用 PascalCase;pages / subPages 下的 index.vue 是路由入口,按目录命名; + * App.vue、layouts 与第三方目录(qiun-*)不参与检查。 + */ +const SRC = join(process.cwd(), "src"); + +// 路由入口目录:其中的 index.vue 是页面 +const ROUTE_DIRS = ["pages", "subPages"]; + +const PASCAL_CASE = /^[A-Z][A-Za-z0-9]*\.vue$/; + +function walk(dir, files = []) { + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) walk(fullPath, files); + else if (entry.name.endsWith(".vue")) files.push(fullPath); + } + return files; +} + +const offenders = []; + +for (const file of walk(SRC)) { + const rel = relative(SRC, file).split(sep).join("/"); + const segments = rel.split("/"); + const name = segments[segments.length - 1]; + + // 第三方组件与框架文件 + if (segments.some((segment) => segment.startsWith("qiun-"))) continue; + if (rel === "App.vue" || rel.startsWith("layouts/")) continue; + + // 路由入口 index.vue,其余 index.vue 视为组件目录入口 + if (name === "index.vue") { + if (!ROUTE_DIRS.includes(segments[0])) offenders.push(rel); + continue; + } + + if (!PASCAL_CASE.test(name)) offenders.push(rel); +} + +if (offenders.length > 0) { + console.log("文件名不符合 PascalCase:"); + for (const file of offenders) console.log(` ${file}`); + console.log("\n自有 Vue 组件用 PascalCase;路由入口保持 index.vue。"); + process.exit(1); +} + +console.log("文件名检查通过(自有 Vue 文件均为 PascalCase)"); diff --git a/scripts/check-templates.mjs b/scripts/check-templates.mjs new file mode 100644 index 0000000..d81db2a --- /dev/null +++ b/scripts/check-templates.mjs @@ -0,0 +1,55 @@ +import { spawnSync } from "node:child_process"; +import { readdirSync, statSync } from "node:fs"; +import { join } from "node:path"; + +/** + * 模板类型检查 + * + * 开启 strictTemplates 后 @wot-ui/ui 内部 SFC 模板会报错,工程无法只针对自有文件检查, + * 因此这里跑 tsconfig.templates.json 并只统计 src 下的错误,第三方报错不参与退出码。 + * + * 组件声明由 vite 插件在 build / dev 时生成,这里只做一次新鲜度提示,避免用过期声明误判。 + */ +const componentsDts = join(process.cwd(), "src", "types", "components.d.ts"); + +// 递归找出 src 下最新的 .vue 修改时间 +function latestVueMtime(dir) { + let latest = 0; + for (const entry of readdirSync(dir, { withFileTypes: true })) { + const fullPath = join(dir, entry.name); + if (entry.isDirectory()) { + latest = Math.max(latest, latestVueMtime(fullPath)); + } else if (entry.name.endsWith(".vue")) { + latest = Math.max(latest, statSync(fullPath).mtimeMs); + } + } + return latest; +} + +try { + const dtsMtime = statSync(componentsDts).mtimeMs; + if (latestVueMtime(join(process.cwd(), "src")) > dtsMtime) { + console.log("提示:src 下有比 src/types/components.d.ts 更新的 .vue,组件声明可能过期"); + console.log(" 先执行 pnpm build:h5 或 pnpm dev:h5 重新生成声明\n"); + } +} catch { + console.log("提示:未找到 src/types/components.d.ts,请先执行 pnpm build:h5 生成声明\n"); +} + +const vueTscBin = join(process.cwd(), "node_modules", "vue-tsc", "bin", "vue-tsc.js"); +const result = spawnSync( + process.execPath, + [vueTscBin, "--noEmit", "-p", "tsconfig.templates.json"], + { encoding: "utf-8" } +); + +const output = `${result.stdout || ""}${result.stderr || ""}`; +const errors = output.split(/\r?\n/).filter((line) => /^src[/\\].*error TS/.test(line)); + +if (errors.length > 0) { + console.log(errors.join("\n")); + console.log(`\n模板类型检查未通过:src 下共 ${errors.length} 处错误`); + process.exit(1); +} + +console.log("模板类型检查通过(只统计 src 下的文件)");