typeorm-codebase-sync
Version:
Automatically update your codebase to add migrations, entities and subscribers to your `data-source.ts` file
119 lines • 5.56 kB
JavaScript
import fs from "fs";
import path from "path";
import ts from "typescript";
import { determineModuleSystemForFile } from "./moduleSystem.js";
import { replaceCodeBlankLinesWithComments } from "./replaceCodeBlankLinesWithComments.js";
import { getRelativeImportPath as getRelativeImportPathUtil } from "./getRelativeImportPath.js";
export class Codebase {
constructor({ entryFilePath, moduleSystem }) {
this.sourceFiles = new Map();
this.updatedSourceFilePaths = new Set();
this.transformedFilesMap = new Map();
this.entryFilePath = entryFilePath;
if (moduleSystem != null)
this.moduleSystem = moduleSystem;
const tsconfigPath = ts.findConfigFile(entryFilePath, ts.sys.fileExists);
const tsconfig = tsconfigPath != null ? ts.readJsonConfigFile(tsconfigPath, ts.sys.readFile) : {};
this.host = ts.createCompilerHost(tsconfig, false);
this._addHostReadFileMiddleware();
this.program = ts.createProgram([this.entryFilePath], tsconfig, this.host);
this.checker = this.program.getTypeChecker();
}
async initialize() {
if (this.initializePromise == null)
this.initializePromise = (async () => {
if (this.moduleSystem == null)
this.moduleSystem = await determineModuleSystemForFile(this.entryFilePath);
})();
await this.initializePromise;
return this;
}
getSourceFile(filePath) {
const absoluteFilePath = path.resolve(filePath);
let sourceFile = this.sourceFiles.get(absoluteFilePath);
if (sourceFile == null) {
sourceFile = this.program.getSourceFile(absoluteFilePath);
if (sourceFile != null)
this.sourceFiles.set(absoluteFilePath, sourceFile);
}
return sourceFile;
}
updateSourceFile(sourceFile) {
const absoluteFilePath = path.resolve(sourceFile.fileName);
this.sourceFiles.set(absoluteFilePath, sourceFile);
return sourceFile;
}
markSourceFileAsUpdated(sourceFile) {
const filePath = path.resolve(sourceFile.fileName);
this.updatedSourceFilePaths.add(filePath);
const knownSourceFile = this.getSourceFile(filePath);
if (knownSourceFile == null)
this.sourceFiles.set(filePath, sourceFile);
return sourceFile;
}
getModifiedSourceFiles() {
const res = [];
for (const filePath of this.updatedSourceFilePaths) {
const sourceFile = this.getSourceFile(filePath);
if (sourceFile != null)
res.push(sourceFile);
}
return res;
}
getRelativeImportPath(containingFilePath, importedFilePath) {
return getRelativeImportPathUtil(containingFilePath, importedFilePath, this.moduleSystem);
}
writeChangesToFilesystem() {
const writtenFilePaths = [];
const pendingFileWrites = [];
this.getSourceFile(this.entryFilePath);
const transformedEntryFile = this.transformedFilesMap.get(path.resolve(this.entryFilePath));
const entryFileUsesCrlf = transformedEntryFile?.content.includes("\r\n") ?? false;
for (const filePath of this.updatedSourceFilePaths) {
const sourceFile = this.getSourceFile(filePath);
if (sourceFile == null)
return [];
const transformedFile = this.transformedFilesMap.get(filePath);
const printer = ts.createPrinter({
newLine: transformedFile == null ? (entryFileUsesCrlf
? ts.NewLineKind.CarriageReturnLineFeed
: ts.NewLineKind.LineFeed) : (transformedFile.content.includes("\r\n")
? ts.NewLineKind.CarriageReturnLineFeed
: ts.NewLineKind.LineFeed),
removeComments: false
});
const printFile = ts.createSourceFile(path.basename(filePath), "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
const compiledFileContent = printer.printNode(ts.EmitHint.Unspecified, sourceFile, printFile);
let resultFileContent = compiledFileContent;
if (transformedFile != null) {
resultFileContent = compiledFileContent
.split(transformedFile.comment)
.join(""); // remove blank-line-preserving comments
}
pendingFileWrites.push({
filePath,
content: resultFileContent
});
}
for (const { filePath, content } of pendingFileWrites) {
fs.writeFileSync(filePath, content, "utf8");
writtenFilePaths.push(filePath);
}
return writtenFilePaths;
}
_addHostReadFileMiddleware() {
const originalHostReadFile = this.host.readFile;
this.host.readFile = (fileName) => {
const absoluteFilePath = path.resolve(fileName);
const extName = path.extname(absoluteFilePath);
if (/^\.[cm]?[jt]s$/.test(extName)) {
// this is needed in order to preserve blank lines after generating the new file from the AST
const transformedFile = replaceCodeBlankLinesWithComments(fs.readFileSync(absoluteFilePath, "utf8"));
this.transformedFilesMap.set(absoluteFilePath, transformedFile);
return transformedFile.content;
}
return originalHostReadFile.call(this.host, fileName);
};
}
}
//# sourceMappingURL=Codebase.js.map