From cff38008e89193d32cc6d9a003c63e6710c7e5ca Mon Sep 17 00:00:00 2001 From: chx7776-afk Date: Mon, 14 Sep 2026 15:41:12 +0800 Subject: [PATCH] fix(web): replace fs.cpSync to avoid Windows crash in extract step `fs.cpSync` in `copyChapterAssets` terminates the Node process on Windows with Node 22. Running `npm run extract` (the `predev` hook of `npm run dev`) prints "Source: root chapter folders (17)" and then exits with STATUS_STACK_BUFFER_OVERRUN (exit code 0xC0000409) without any JS-level error, so the course data is never written and the dev server never starts. Replace it with a small recursive copy helper built on `fs.readdirSync` + `fs.copyFileSync`, which is what `fs.cpSync` does internally minus the native path that crashes here. Verified on Windows 11 / Node v22.20.0: - before: exit 0xC0000409, extraction aborted, no course-assets output - after: exit 0, "17 versions / 16 diffs / 51 docs", 74 asset files written --- web/scripts/extract-content.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/web/scripts/extract-content.ts b/web/scripts/extract-content.ts index 8108bc5fb..4e18f5f86 100644 --- a/web/scripts/extract-content.ts +++ b/web/scripts/extract-content.ts @@ -204,6 +204,26 @@ function titleFromMarkdown(content: string, fallback: string): string { return titleMatch ? titleMatch[1] : fallback; } +/** + * Recursively copy a directory tree. + * + * Avoids `fs.cpSync`: on Windows with Node 22 it terminates the process with + * STATUS_STACK_BUFFER_OVERRUN (exit code 0xC0000409) and no JS-level error, + * which silently breaks `npm run dev` because this script backs its `predev` step. + */ +function copyDirRecursive(srcDir: string, dstDir: string): void { + fs.mkdirSync(dstDir, { recursive: true }); + for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) { + const srcPath = path.join(srcDir, entry.name); + const dstPath = path.join(dstDir, entry.name); + if (entry.isDirectory()) { + copyDirRecursive(srcPath, dstPath); + } else if (entry.isFile()) { + fs.copyFileSync(srcPath, dstPath); + } + } +} + function cleanCourseAssets() { fs.rmSync(COURSE_ASSETS_DIR, { recursive: true, force: true }); fs.mkdirSync(COURSE_ASSETS_DIR, { recursive: true }); @@ -215,7 +235,7 @@ function copyChapterAssets(chapter: ChapterSource): ChapterImage[] { const outDir = path.join(COURSE_ASSETS_DIR, chapter.dirName); fs.mkdirSync(outDir, { recursive: true }); - fs.cpSync(imagesDir, outDir, { recursive: true }); + copyDirRecursive(imagesDir, outDir); return fs .readdirSync(imagesDir)