prisma-json-types-generator
Version:
Changes JsonValues to your custom typescript type
112 lines (94 loc) • 3.09 kB
text/typescript
import fs from 'fs/promises';
import { PrismaJsonTypesGeneratorConfig } from './config';
import { NAMESPACE_PATH } from './constants';
import { PrismaJsonTypesGeneratorError } from './error';
/** A changes made in the original file to help adjust any future coordinates of texts */
export interface TextDiff {
start: number;
diff: number;
}
/**
* A class to help with reading and writing the Prisma Client types file concurrently and
* converting positions indexes according with previous changes.
*/
export class DeclarationWriter {
constructor(
readonly filepath: string,
private readonly options: PrismaJsonTypesGeneratorConfig
) {}
/** The prisma's index.d.ts file content. */
public content = '';
private changeset: TextDiff[] = [];
async template() {
let namespace = await fs.readFile(NAMESPACE_PATH, 'utf-8');
// Removes trailing spaces
namespace = namespace.trim();
// Replaces the namespace with the provided namespace
namespace = namespace.replace(/\$\$NAMESPACE\$\$/g, this.options.namespace);
// Includes previous file content
return namespace + '\n' + this.content;
}
/** Loads the original file of sourcePath into memory. */
async load() {
if (!(await fs.stat(this.filepath))) {
throw new PrismaJsonTypesGeneratorError(
'Tried to load a file that does not exist',
{ filepath: this.filepath }
);
}
if (this.changeset.length) {
throw new PrismaJsonTypesGeneratorError(
'Tried to load a file that has already been changed',
{ filepath: this.filepath, changeset: this.changeset }
);
}
this.content = await fs.readFile(this.filepath, 'utf-8');
}
/** Save the original file of sourcePath with the content's contents */
async save() {
// Resets current changeset and file content
this.content = await this.template();
this.changeset = [];
// Writes it into the disk
await fs.writeFile(this.filepath, this.content);
}
/**
* Replaces the coordinates with the provided text, adjusting the coords to previous
* changes.
*
* @example
*
* ```txt
* a 1 1 a
* s 2 2 s
* d 3 3 s <- (start: 1, end: 3, text: `s`) changed `s` to `ss` (start: 1, wide: 2)
* 4 4 d
* 5 5
* a 6 6
* s 7 7 a
* d 8 8 s
* 9 9 d
* ```
*/
replace(start: number, end: number, text: string) {
// Adds a trailing space
if (text[0] !== ' ') {
text = ' ' + text;
}
// Maps the coordinates to the previous changes to adjust the position for the new text
for (const change of this.changeset) {
if (start > change.start) {
start += change.diff;
end += change.diff;
}
}
// Replaces the file content at the correct position
this.content = this.content.slice(0, start) + text + this.content.slice(end);
// Adds the change to the list
this.changeset.push({
start,
// The difference between the old text and the new text
diff: start - end + text.length
});
}
}