fix(web): replace fs.cpSync to avoid Windows crash in extract step (#574)

`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
This commit is contained in:
chx7776-afk
2026-10-27 12:40:19 +08:00
committed by GitHub
parent 45a0f3585a
commit e1a35baa8b
+21 -1
View File
@@ -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)