Skip to content

Commit c134ba7

Browse files
committed
add in-memory buffer for writer and in-memory work mode.
1 parent 0204266 commit c134ba7

3 files changed

Lines changed: 57 additions & 26 deletions

File tree

src/api/builder.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { extractNameFromCanonical, packageMetaToFhir, packageMetaToNpm, type Typ
1717
import type { TypeSchemaConfig } from "../config";
1818
import { CodegenLogger, createLogger } from "../utils/codegen-logger";
1919
import { TypeScript, type TypeScriptOptions } from "./writer-generator/typescript";
20-
import type { FileSystemWriter, WriterOptions } from "./writer-generator/writer";
20+
import type { FileBuffer, FileSystemWriter, WriterOptions } from "./writer-generator/writer";
2121

2222
/**
2323
* Configuration options for the API builder
@@ -66,7 +66,7 @@ const normalizeFileName = (str: string): string => {
6666
return res;
6767
};
6868

69-
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
69+
export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
7070

7171
type APIBuilderConfig = PartialBy<
7272
Required<APIBuilderOptions>,
@@ -409,8 +409,8 @@ export class APIBuilder {
409409

410410
try {
411411
await generator.generate(tsIndex);
412-
const files: string[] = generator.writtenFiles();
413-
result.filesGenerated.push(...files);
412+
const fileBuffer: FileBuffer[] = generator.writtenFiles();
413+
result.filesGenerated.push(...fileBuffer.map((e) => e.absPath));
414414
this.logger.info(`Generating ${type} finished successfully`);
415415
} catch (error) {
416416
result.errors.push(

src/api/writer-generator/csharp/csharp.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import fs from "node:fs";
22
import Path from "node:path";
3+
import type { PartialBy } from "@root/api/builder.js";
34
import { pascalCase, uppercaseFirstLetter, uppercaseFirstLetterOfEach } from "@root/api/writer-generator/utils.ts";
4-
import { type PartialBy, Writer, type WriterOptions } from "@root/api/writer-generator/writer.ts";
5+
import { Writer, type WriterOptions } from "@root/api/writer-generator/writer.ts";
56
import type { Field, Identifier, RegularField } from "@typeschema/types";
67
import { type ChoiceFieldInstance, isChoiceDeclarationField, type RegularTypeSchema } from "@typeschema/types.ts";
78
import type { TypeSchemaIndex } from "@typeschema/utils.ts";

src/api/writer-generator/writer.ts

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { CodegenLogger } from "@root/utils/codegen-logger";
55

66
export type FileSystemWriterOptions = {
77
outputDir: string;
8+
inMemoryOnly?: boolean;
89
logger?: CodegenLogger;
910
};
1011

@@ -15,13 +16,14 @@ export type WriterOptions = FileSystemWriterOptions & {
1516
generateProfile?: boolean;
1617
};
1718

18-
export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
19+
type FileBufferInternal = { relPath: string; absPath: string; tokens: string[] };
20+
export type FileBuffer = { relPath: string; absPath: string; content: string };
1921

2022
export abstract class FileSystemWriter<T extends FileSystemWriterOptions = FileSystemWriterOptions> {
2123
opts: T;
2224
currentDir?: string;
23-
currentFileDescriptor?: number;
24-
writtenFilesSet: Set<string> = new Set();
25+
currentFile?: { relPath: string; descriptor: number };
26+
writtenFilesBuffer: Record<string, FileBufferInternal> = {};
2527

2628
constructor(opts: T) {
2729
this.opts = opts;
@@ -36,48 +38,76 @@ export abstract class FileSystemWriter<T extends FileSystemWriterOptions = FileS
3638
return this.opts.logger;
3739
}
3840

41+
onDiskMkDir(path: string) {
42+
if (this.opts.inMemoryOnly) return;
43+
if (!fs.existsSync(path)) {
44+
fs.mkdirSync(path, { recursive: true });
45+
}
46+
}
47+
48+
onDiskOpenFile(relPath: string): number {
49+
if (this.opts.inMemoryOnly) return -1;
50+
return fs.openSync(relPath, "w");
51+
}
52+
53+
onDiskCloseFile(descriptor: number) {
54+
if (this.opts.inMemoryOnly) return;
55+
fs.fsyncSync(descriptor);
56+
fs.closeSync(descriptor);
57+
}
58+
59+
onDiskWrite(descriptor: number, token: string) {
60+
if (this.opts.inMemoryOnly) return;
61+
fs.writeSync(descriptor, token);
62+
}
63+
3964
cd(path: string, gen: () => void) {
4065
const prev = this.currentDir;
4166
this.currentDir = path.startsWith("/")
4267
? Path.join(this.opts.outputDir, path)
4368
: Path.join(this.currentDir ?? this.opts.outputDir, path);
44-
if (!fs.existsSync(this.currentDir)) {
45-
fs.mkdirSync(this.currentDir, { recursive: true });
46-
}
69+
this.onDiskMkDir(this.currentDir);
4770
this.logger()?.debug(`cd '${this.currentDir}'`);
4871
gen();
4972
this.currentDir = prev;
5073
}
5174

5275
cat(fn: string, gen: () => void) {
53-
if (this.currentFileDescriptor) throw new Error("Can't open file in file");
76+
if (this.currentFile) throw new Error("Can't open file when another file is open");
5477
if (fn.includes("/")) throw new Error(`Change file path separatly: ${fn}`);
5578

56-
const fullFn = `${this.currentDir}/${fn}`;
79+
const relPath = Path.normalize(`${this.currentDir}/${fn}`);
5780
try {
58-
this.currentFileDescriptor = fs.openSync(fullFn, "w");
59-
this.writtenFilesSet.add(Path.resolve(Path.normalize(fullFn)));
60-
this.logger()?.debug(`cat > '${fullFn}'`);
81+
const descriptor = this.onDiskOpenFile(relPath);
82+
83+
this.logger()?.debug(`cat > '${relPath}'`);
84+
this.currentFile = { descriptor, relPath };
85+
this.writtenFilesBuffer[this.currentFile.relPath] = { relPath, absPath: Path.resolve(relPath), tokens: [] };
86+
6187
gen();
6288
} finally {
63-
if (this.currentFileDescriptor) {
64-
// Force flush all buffered data to disk before closing
65-
fs.fsyncSync(this.currentFileDescriptor);
66-
fs.closeSync(this.currentFileDescriptor);
67-
}
68-
this.currentFileDescriptor = undefined;
89+
if (this.currentFile) this.onDiskCloseFile(this.currentFile.descriptor);
90+
this.currentFile = undefined;
6991
}
7092
}
7193

7294
write(str: string) {
73-
if (!this.currentFileDescriptor) throw new Error("No file opened");
74-
fs.writeSync(this.currentFileDescriptor, str);
95+
if (!this.currentFile) throw new Error("No file opened");
96+
this.onDiskWrite(this.currentFile.descriptor, str);
97+
98+
const buf = this.writtenFilesBuffer[this.currentFile.relPath];
99+
if (!buf) throw new Error("No buffer found");
100+
buf.tokens.push(str);
75101
}
76102

77103
abstract generate(_tsIndex: TypeSchemaIndex): Promise<void>;
78104

79-
writtenFiles(): string[] {
80-
return Array.from(this.writtenFilesSet);
105+
writtenFiles(): FileBuffer[] {
106+
return Object.values(this.writtenFilesBuffer)
107+
.map(({ relPath, absPath, tokens }) => {
108+
return { relPath, absPath, content: tokens.join() };
109+
})
110+
.sort((a, b) => a.relPath.localeCompare(b.relPath));
81111
}
82112
}
83113

0 commit comments

Comments
 (0)