Skip to content

Commit 8cf1324

Browse files
authored
Merge pull request microsoft#34521 from amcasey/LazyDirectoryCreation
Stop pre-emptively creating directories
2 parents 778a757 + af2f46e commit 8cf1324

File tree

4 files changed

+78
-64
lines changed

4 files changed

+78
-64
lines changed

src/compiler/program.ts

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -96,35 +96,51 @@ namespace ts {
9696
if (existingDirectories.has(directoryPath)) {
9797
return true;
9898
}
99-
if (system.directoryExists(directoryPath)) {
99+
if ((compilerHost.directoryExists || system.directoryExists)(directoryPath)) {
100100
existingDirectories.set(directoryPath, true);
101101
return true;
102102
}
103103
return false;
104104
}
105105

106-
function ensureDirectoriesExist(directoryPath: string) {
107-
if (directoryPath.length > getRootLength(directoryPath) && !directoryExists(directoryPath)) {
108-
const parentDirectory = getDirectoryPath(directoryPath);
109-
ensureDirectoriesExist(parentDirectory);
110-
if (compilerHost.createDirectory) {
111-
compilerHost.createDirectory(directoryPath);
112-
}
113-
else {
114-
system.createDirectory(directoryPath);
106+
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) {
107+
try {
108+
performance.mark("beforeIOWrite");
109+
110+
// NOTE: If patchWriteFileEnsuringDirectory has been called,
111+
// the system.writeFile will do its own directory creation and
112+
// the ensureDirectoriesExist call will always be redundant.
113+
writeFileEnsuringDirectories(
114+
fileName,
115+
data,
116+
writeByteOrderMark,
117+
(path, data, writeByteOrderMark) => writeFileWorker(path, data, writeByteOrderMark),
118+
path => (compilerHost.createDirectory || system.createDirectory)(path),
119+
path => directoryExists(path));
120+
121+
performance.mark("afterIOWrite");
122+
performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
123+
}
124+
catch (e) {
125+
if (onError) {
126+
onError(e.message);
115127
}
116128
}
117129
}
118130

119131
let outputFingerprints: Map<OutputFingerprint>;
132+
function writeFileWorker(fileName: string, data: string, writeByteOrderMark: boolean) {
133+
if (!isWatchSet(options) || !system.createHash || !system.getModifiedTime) {
134+
system.writeFile(fileName, data, writeByteOrderMark);
135+
return;
136+
}
120137

121-
function writeFileIfUpdated(fileName: string, data: string, writeByteOrderMark: boolean): void {
122138
if (!outputFingerprints) {
123139
outputFingerprints = createMap<OutputFingerprint>();
124140
}
125141

126-
const hash = system.createHash!(data); // TODO: GH#18217
127-
const mtimeBefore = system.getModifiedTime!(fileName); // TODO: GH#18217
142+
const hash = system.createHash(data);
143+
const mtimeBefore = system.getModifiedTime(fileName);
128144

129145
if (mtimeBefore) {
130146
const fingerprint = outputFingerprints.get(fileName);
@@ -139,7 +155,7 @@ namespace ts {
139155

140156
system.writeFile(fileName, data, writeByteOrderMark);
141157

142-
const mtimeAfter = system.getModifiedTime!(fileName) || missingFileModifiedTime; // TODO: GH#18217
158+
const mtimeAfter = system.getModifiedTime(fileName) || missingFileModifiedTime;
143159

144160
outputFingerprints.set(fileName, {
145161
hash,
@@ -148,28 +164,6 @@ namespace ts {
148164
});
149165
}
150166

151-
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) {
152-
try {
153-
performance.mark("beforeIOWrite");
154-
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
155-
156-
if (isWatchSet(options) && system.createHash && system.getModifiedTime) {
157-
writeFileIfUpdated(fileName, data, writeByteOrderMark);
158-
}
159-
else {
160-
system.writeFile(fileName, data, writeByteOrderMark);
161-
}
162-
163-
performance.mark("afterIOWrite");
164-
performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");
165-
}
166-
catch (e) {
167-
if (onError) {
168-
onError(e.message);
169-
}
170-
}
171-
}
172-
173167
function getDefaultLibLocation(): string {
174168
return getDirectoryPath(normalizePath(system.getExecutingFilePath()));
175169
}

src/compiler/sys.ts

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -522,31 +522,21 @@ namespace ts {
522522
}
523523
}
524524

525-
function recursiveCreateDirectory(directoryPath: string, sys: System) {
526-
const basePath = getDirectoryPath(directoryPath);
527-
const shouldCreateParent = basePath !== "" && directoryPath !== basePath && !sys.directoryExists(basePath);
528-
if (shouldCreateParent) {
529-
recursiveCreateDirectory(basePath, sys);
530-
}
531-
if (shouldCreateParent || !sys.directoryExists(directoryPath)) {
532-
sys.createDirectory(directoryPath);
533-
}
534-
}
535-
536525
/**
537526
* patch writefile to create folder before writing the file
538527
*/
539528
/*@internal*/
540529
export function patchWriteFileEnsuringDirectory(sys: System) {
541530
// patch writefile to create folder before writing the file
542531
const originalWriteFile = sys.writeFile;
543-
sys.writeFile = (path, data, writeBom) => {
544-
const directoryPath = getDirectoryPath(normalizeSlashes(path));
545-
if (directoryPath && !sys.directoryExists(directoryPath)) {
546-
recursiveCreateDirectory(directoryPath, sys);
547-
}
548-
originalWriteFile.call(sys, path, data, writeBom);
549-
};
532+
sys.writeFile = (path, data, writeBom) =>
533+
writeFileEnsuringDirectories(
534+
path,
535+
data,
536+
!!writeBom,
537+
(path, data, writeByteOrderMark) => originalWriteFile.call(sys, path, data, writeByteOrderMark),
538+
path => sys.createDirectory(path),
539+
path => sys.directoryExists(path));
550540
}
551541

552542
/*@internal*/

src/compiler/utilities.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3700,6 +3700,36 @@ namespace ts {
37003700
}, sourceFiles);
37013701
}
37023702

3703+
function ensureDirectoriesExist(
3704+
directoryPath: string,
3705+
createDirectory: (path: string) => void,
3706+
directoryExists: (path: string) => boolean): void {
3707+
if (directoryPath.length > getRootLength(directoryPath) && !directoryExists(directoryPath)) {
3708+
const parentDirectory = getDirectoryPath(directoryPath);
3709+
ensureDirectoriesExist(parentDirectory, createDirectory, directoryExists);
3710+
createDirectory(directoryPath);
3711+
}
3712+
}
3713+
3714+
export function writeFileEnsuringDirectories(
3715+
path: string,
3716+
data: string,
3717+
writeByteOrderMark: boolean,
3718+
writeFile: (path: string, data: string, writeByteOrderMark: boolean) => void,
3719+
createDirectory: (path: string) => void,
3720+
directoryExists: (path: string) => boolean): void {
3721+
3722+
// PERF: Checking for directory existence is expensive. Instead, assume the directory exists
3723+
// and fall back to creating it if the file write fails.
3724+
try {
3725+
writeFile(path, data, writeByteOrderMark);
3726+
}
3727+
catch {
3728+
ensureDirectoriesExist(getDirectoryPath(normalizePath(path)), createDirectory, directoryExists);
3729+
writeFile(path, data, writeByteOrderMark);
3730+
}
3731+
}
3732+
37033733
export function getLineOfLocalPosition(currentSourceFile: SourceFile, pos: number) {
37043734
return getLineAndCharacterOfPosition(currentSourceFile, pos).line;
37053735
}

src/compiler/watch.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -303,20 +303,20 @@ namespace ts {
303303
readDirectory: maybeBind(host, host.readDirectory),
304304
};
305305

306-
function ensureDirectoriesExist(directoryPath: string) {
307-
if (directoryPath.length > getRootLength(directoryPath) && !host.directoryExists!(directoryPath)) {
308-
const parentDirectory = getDirectoryPath(directoryPath);
309-
ensureDirectoriesExist(parentDirectory);
310-
if (host.createDirectory) host.createDirectory(directoryPath);
311-
}
312-
}
313-
314306
function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) {
315307
try {
316308
performance.mark("beforeIOWrite");
317-
ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName)));
318309

319-
host.writeFile!(fileName, text, writeByteOrderMark);
310+
// NOTE: If patchWriteFileEnsuringDirectory has been called,
311+
// the host.writeFile will do its own directory creation and
312+
// the ensureDirectoriesExist call will always be redundant.
313+
writeFileEnsuringDirectories(
314+
fileName,
315+
text,
316+
writeByteOrderMark,
317+
(path, data, writeByteOrderMark) => host.writeFile!(path, data, writeByteOrderMark),
318+
path => host.createDirectory!(path),
319+
path => host.directoryExists!(path));
320320

321321
performance.mark("afterIOWrite");
322322
performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite");

0 commit comments

Comments
 (0)