From b9c98fa39bfcd02e4b967e57cc09cf3ff64473be Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 8 Feb 2026 18:57:06 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9A=A1=20optimize=20docs-tree=20API=20wi?= =?UTF-8?q?th=20asynchronous=20I/O=20and=20parallel=20processing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Refactor `safeListDir` and `buildTree` to use `fs.promises.readdir`. - Implement parallel directory traversal using `Promise.all`. - Update `GET` handler to use asynchronous `fs.promises.access` for candidate selection. - Ensure the event loop is not blocked during I/O operations. Co-authored-by: longsizhuo <114939201+longsizhuo@users.noreply.github.com> --- app/api/docs-tree/route.ts | 50 ++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/app/api/docs-tree/route.ts b/app/api/docs-tree/route.ts index c42fe62d..09bd2d56 100644 --- a/app/api/docs-tree/route.ts +++ b/app/api/docs-tree/route.ts @@ -39,29 +39,41 @@ function hasFs() { } } -function safeListDir(dir: string): { entries: fs.Dirent[]; error?: string } { +async function safeListDir( + dir: string, +): Promise<{ entries: fs.Dirent[]; error?: string }> { try { - return { entries: fs.readdirSync(dir, { withFileTypes: true }) }; + const entries = await fs.promises.readdir(dir, { withFileTypes: true }); + return { entries }; } catch (e) { return { entries: [], error: String(e) }; } } -function buildTree(root: string, maxDepth = 2, rel = ""): DirNode[] { - const { entries, error } = safeListDir(root); +async function buildTree( + root: string, + maxDepth = 2, + rel = "", +): Promise { + const { entries, error } = await safeListDir(root); if (error) throw new Error(`readdir failed at ${root}: ${error}`); const dirs = entries.filter((d) => d.isDirectory()); - const nodes: DirNode[] = []; - for (const e of dirs) { - if (e.name.startsWith(".") || e.name.startsWith("[")) continue; + const nodePromises = dirs.map(async (e) => { + if (e.name.startsWith(".") || e.name.startsWith("[")) return null; const abs = path.join(root, e.name); const nodeRel = rel ? `${rel}/${e.name}` : e.name; const node: DirNode = { name: e.name, path: nodeRel }; - if (maxDepth > 1) node.children = buildTree(abs, maxDepth - 1, nodeRel); - nodes.push(node); - } + if (maxDepth > 1) { + node.children = await buildTree(abs, maxDepth - 1, nodeRel); + } + return node; + }); + + const nodes = (await Promise.all(nodePromises)).filter( + (n): n is DirNode => n !== null, + ); try { nodes.sort((a, b) => a.name.localeCompare(b.name, "zh-Hans")); @@ -98,7 +110,19 @@ export async function GET() { } // pick the first existing candidate - const docsRoot = candidates.find((p) => fs.existsSync(p)); + let docsRoot: string | undefined; + const candidateChecks = await Promise.all( + candidates.map(async (p) => { + try { + await fs.promises.access(p); + return { p, exists: true }; + } catch { + return { p, exists: false }; + } + }), + ); + docsRoot = candidateChecks.find((c) => c.exists)?.p; + if (!docsRoot) { return NextResponse.json( { @@ -107,7 +131,7 @@ export async function GET() { diag: { ...diag, exists: Object.fromEntries( - candidates.map((p) => [p, fs.existsSync(p)]), + candidateChecks.map((c) => [c.p, c.exists]), ), }, }, @@ -118,7 +142,7 @@ export async function GET() { // try to list let tree: DirNode[] = []; try { - tree = buildTree(docsRoot, 2); + tree = await buildTree(docsRoot, 2); } catch (e: unknown) { const msg = e instanceof Error ? e.message : String(e); return NextResponse.json( From b46f6913c0c2021f62dc4e8d302b127967d97ec4 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 8 Feb 2026 19:02:37 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9A=A1=20optimize=20docs-tree=20API=20an?= =?UTF-8?q?d=20fix=20CI=20lint=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Refactor `app/api/docs-tree/route.ts` to use asynchronous I/O and parallel processing. - Fix ESLint `prefer-const` errors in `app/api/docs-tree/route.ts`. - Remove unused `GitHub` import in `auth.config.ts`. - Fix unused `error` variable in `scripts/check-pnpm-version.mjs`. Co-authored-by: longsizhuo <114939201+longsizhuo@users.noreply.github.com> --- app/api/docs-tree/route.ts | 9 +++------ auth.config.ts | 1 - scripts/check-pnpm-version.mjs | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/app/api/docs-tree/route.ts b/app/api/docs-tree/route.ts index 09bd2d56..3a9c93f5 100644 --- a/app/api/docs-tree/route.ts +++ b/app/api/docs-tree/route.ts @@ -110,7 +110,6 @@ export async function GET() { } // pick the first existing candidate - let docsRoot: string | undefined; const candidateChecks = await Promise.all( candidates.map(async (p) => { try { @@ -121,7 +120,7 @@ export async function GET() { } }), ); - docsRoot = candidateChecks.find((c) => c.exists)?.p; + const docsRoot = candidateChecks.find((c) => c.exists)?.p; if (!docsRoot) { return NextResponse.json( @@ -140,9 +139,9 @@ export async function GET() { } // try to list - let tree: DirNode[] = []; try { - tree = await buildTree(docsRoot, 2); + const tree = await buildTree(docsRoot, 2); + return NextResponse.json({ ok: true, docsRoot, tree, diag }); } catch (e: unknown) { const msg = e instanceof Error ? e.message : String(e); return NextResponse.json( @@ -155,8 +154,6 @@ export async function GET() { { status: 500 }, ); } - - return NextResponse.json({ ok: true, docsRoot, tree, diag }); } catch (err: unknown) { const msg = err instanceof Error ? err.message : String(err); return NextResponse.json( diff --git a/auth.config.ts b/auth.config.ts index 6813b1df..719d86b7 100644 --- a/auth.config.ts +++ b/auth.config.ts @@ -1,5 +1,4 @@ import type { NextAuthConfig } from "next-auth"; -import GitHub from "next-auth/providers/github"; // 在本地开发环境允许没有 .env 的协作者运行站点,因此先尝试读取两个常见的密钥变量,缺失时再使用内置的开发兜底值。 const envSecret = process.env.AUTH_SECRET ?? process.env.NEXTAUTH_SECRET; diff --git a/scripts/check-pnpm-version.mjs b/scripts/check-pnpm-version.mjs index 693ffc82..3393aa3a 100755 --- a/scripts/check-pnpm-version.mjs +++ b/scripts/check-pnpm-version.mjs @@ -54,7 +54,7 @@ try { let actualVersion; try { actualVersion = execSync('pnpm --version', { encoding: 'utf-8' }).trim(); - } catch (error) { + } catch { console.error('❌ Error: pnpm is not installed or not found in PATH'); console.error('\nTo fix this issue:'); console.error('1. Enable corepack: corepack enable'); From 802cef6983dd981a8cee6be8e33a0461e0baa4be Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 8 Feb 2026 19:05:16 +0000 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9A=A1=20optimize=20docs-tree=20API=20wi?= =?UTF-8?q?th=20asynchronous=20I/O=20and=20parallel=20processing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Refactor `app/api/docs-tree/route.ts` to use asynchronous file system operations. - Implement parallel directory traversal using `Promise.all` for improved efficiency. - Add Chinese comments to explain the asynchronous parallel logic. - Fix CI lint errors (prefer-const, unused variables). Co-authored-by: longsizhuo <114939201+longsizhuo@users.noreply.github.com> --- app/api/docs-tree/route.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/api/docs-tree/route.ts b/app/api/docs-tree/route.ts index 3a9c93f5..77e6cd29 100644 --- a/app/api/docs-tree/route.ts +++ b/app/api/docs-tree/route.ts @@ -60,17 +60,20 @@ async function buildTree( const dirs = entries.filter((d) => d.isDirectory()); + // 并行处理所有子目录以提高 I/O 效率 const nodePromises = dirs.map(async (e) => { if (e.name.startsWith(".") || e.name.startsWith("[")) return null; const abs = path.join(root, e.name); const nodeRel = rel ? `${rel}/${e.name}` : e.name; const node: DirNode = { name: e.name, path: nodeRel }; if (maxDepth > 1) { + // 递归构建子树 node.children = await buildTree(abs, maxDepth - 1, nodeRel); } return node; }); + // 等待所有并行任务完成,并过滤掉被忽略的目录 const nodes = (await Promise.all(nodePromises)).filter( (n): n is DirNode => n !== null, ); @@ -109,10 +112,11 @@ export async function GET() { ); } - // pick the first existing candidate + // 异步并行检查所有候选路径是否存在 const candidateChecks = await Promise.all( candidates.map(async (p) => { try { + // 使用 access 检查路径是否可读 await fs.promises.access(p); return { p, exists: true }; } catch { @@ -120,6 +124,7 @@ export async function GET() { } }), ); + // 从检查结果中找到第一个存在的路径 const docsRoot = candidateChecks.find((c) => c.exists)?.p; if (!docsRoot) {