Skip to content

Commit fc1caff

Browse files
committed
don't clobber an existing file when building the site
closes #1367 supersedes #1369 supersedes #1373
1 parent 3578dff commit fc1caff

File tree

1 file changed

+14
-2
lines changed

1 file changed

+14
-2
lines changed

src/build.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import {createHash} from "node:crypto";
22
import {existsSync} from "node:fs";
3-
import {access, constants, copyFile, readFile, stat, writeFile} from "node:fs/promises";
3+
import {access, constants, copyFile, readFile, realpath, stat, writeFile} from "node:fs/promises";
44
import {basename, dirname, extname, join} from "node:path/posix";
55
import type {Config} from "./config.js";
66
import {CliError, isEnoent} from "./error.js";
7-
import {getClientPath, prepareOutput, visitMarkdownFiles} from "./files.js";
7+
import {getClientPath, maybeStat, prepareOutput, visitMarkdownFiles} from "./files.js";
88
import {getModuleHash} from "./javascript/module.js";
99
import {transpileModule} from "./javascript/transpile.js";
1010
import type {Logger, Writer} from "./logger.js";
@@ -345,13 +345,25 @@ export class FileBuildEffects implements BuildEffects {
345345
async copyFile(sourcePath: string, outputPath: string): Promise<void> {
346346
const destination = join(this.outputRoot, outputPath);
347347
this.logger.log(destination);
348+
await noClobber(destination);
348349
await prepareOutput(destination);
349350
await copyFile(sourcePath, destination);
350351
}
351352
async writeFile(outputPath: string, contents: string | Buffer): Promise<void> {
352353
const destination = join(this.outputRoot, outputPath);
353354
this.logger.log(destination);
355+
await noClobber(destination);
354356
await prepareOutput(destination);
355357
await writeFile(destination, contents);
356358
}
357359
}
360+
361+
// Asserts that the destination file is not already present. This can happen if
362+
// the same file is referenced with inconsistent naming (e.g. upper vs.
363+
// lowercase), and would result in a broken site.
364+
async function noClobber(path) {
365+
if (await maybeStat(path)) {
366+
const real = (await realpath(path)).slice((await realpath(".")).length + 1);
367+
throw new Error(`${faint("File name conflict over")} ${real}.`);
368+
}
369+
}

0 commit comments

Comments
 (0)