From 2821ca6eb008fab4c978b3ef5292969a66aa610f Mon Sep 17 00:00:00 2001 From: Emilien Escalle Date: Sat, 9 Nov 2024 07:27:20 +0100 Subject: [PATCH] feat: use es2022 Signed-off-by: Emilien Escalle --- src/services/file/FileFactory.spec.ts | 108 +++++++++++++++++++ src/services/file/FileFactory.ts | 6 +- src/services/file/JsonFile.ts | 44 +++++++- src/services/file/TomlFile.ts | 57 ++++++++-- src/services/file/TypescriptFile.ts | 49 +++++---- src/services/template/adapters/EtaAdapter.ts | 18 ++-- tsconfig.json | 2 +- 7 files changed, 242 insertions(+), 42 deletions(-) create mode 100644 src/services/file/FileFactory.spec.ts diff --git a/src/services/file/FileFactory.spec.ts b/src/services/file/FileFactory.spec.ts new file mode 100644 index 00000000..96dd6241 --- /dev/null +++ b/src/services/file/FileFactory.spec.ts @@ -0,0 +1,108 @@ +import { writeFile } from "fs/promises"; +import { resolve } from "path"; + +import container from "../../container"; +import { DirResult, createTmpDir } from "../../tests/tmp-dir"; +import { FileFactory } from "./FileFactory"; +import { JsonFile } from "./JsonFile"; +import { TomlFile } from "./TomlFile"; +import { TypescriptFile } from "./TypescriptFile"; +import { StdFile } from "./StdFile"; + +describe("FileFactory", () => { + let testDir: DirResult; + let fileFactory: FileFactory; + + beforeEach(() => { + // Initialize service before each test to not be confused by cache + container.snapshot(); + fileFactory = container.get(FileFactory); + }); + + afterEach(() => { + container.restore(); + testDir?.removeCallback(); + }); + + describe("fromFile", () => { + it("should create a file from a file path", async () => { + testDir = await createTmpDir(); + + const filepath = resolve(testDir.name, "test.txt"); + await writeFile(filepath, "test"); + + const file = await fileFactory.fromFile(filepath); + expect(file).toBeInstanceOf(StdFile); + + const content = await file.getContent(); + expect(content).toBe("test"); + }); + + it("should create a file from a file path with encoding", async () => { + testDir = await createTmpDir(); + + const filepath = resolve(testDir.name, "test.txt"); + await writeFile(filepath, "test"); + + const file = await fileFactory.fromFile(filepath, "utf8"); + expect(file).toBeInstanceOf(StdFile); + + const content = await file.getContent(); + expect(content).toBe("test"); + }); + + it("should throw an error when the file does not exist", async () => { + await expect(fileFactory.fromFile("non-existing-file")).rejects.toThrow( + 'File "non-existing-file" does not exist' + ); + }); + }); + + describe("fromString", () => { + it("should create a file from a string", async () => { + const file = await fileFactory.fromString("test", "test.txt"); + + expect(file).toBeInstanceOf(StdFile); + expect(await file.getContent()).toBe("test"); + }); + + it("should create a file from a string with encoding", async () => { + const file = await fileFactory.fromString("test", "test.txt", "utf8"); + + expect(file).toBeInstanceOf(StdFile); + expect(await file.getContent()).toBe("test"); + }); + + it("should create a Json file from a string", async () => { + const file = await fileFactory.fromString('{"test": "test"}', "test.json"); + + expect(file).toBeInstanceOf(JsonFile); + const jsonFile = file as JsonFile; + + expect(await jsonFile.getContent()).toBe('{"test": "test"}'); + expect(await jsonFile.getData()).toEqual({ test: "test" }); + expect(await jsonFile.getData("test")).toEqual("test"); + expect(await jsonFile.getData("unknown")).toBeUndefined(); + }); + + it("should create a Toml file from a string", async () => { + const file = await fileFactory.fromString("test = 'test'", "test.toml"); + + expect(file).toBeInstanceOf(TomlFile); + expect(await file.getContent()).toBe("test = 'test'"); + expect(await (file as TomlFile).getData()).toEqual({ test: "test" }); + expect(await (file as TomlFile).getData("test")).toEqual("test"); + expect(await (file as TomlFile).getData("unknown")).toBeUndefined(); + }); + + it("should create a Typescript file from a string", async () => { + const file = await fileFactory.fromString("console.log('test')", "test.ts"); + + expect(file).toBeInstanceOf(TypescriptFile); + expect(await file.getContent()).toBe("console.log('test')"); + }); + }); +}); +function writeFileSync(filepath: string, arg1: string) { + throw new Error("Function not implemented."); +} diff --git a/src/services/file/FileFactory.ts b/src/services/file/FileFactory.ts index c53f1cc0..c868a80f 100644 --- a/src/services/file/FileFactory.ts +++ b/src/services/file/FileFactory.ts @@ -61,7 +61,11 @@ export class FileFactory { } } - fromString(content: string, file: string, encoding: BufferEncoding = "utf8"): StdFile { + fromString( + content: string, + file: string, + encoding: BufferEncoding = "utf8" + ): StdFile | JsonFile | TomlFile | TypescriptFile { const args = [ this.cliService, this.directoryService, diff --git a/src/services/file/JsonFile.ts b/src/services/file/JsonFile.ts index 66958797..164afb9f 100644 --- a/src/services/file/JsonFile.ts +++ b/src/services/file/JsonFile.ts @@ -1,6 +1,11 @@ import { all } from "deepmerge"; import { StdFile } from "./StdFile"; +import { CliService } from "../CliService"; +import { DirectoryService } from "./DirectoryService"; +import { FileDiffService } from "./FileDiffService"; +import { FileFactory } from "./FileFactory"; +import { FileService } from "./FileService"; type JsonArray = boolean[] | number[] | string[] | JsonFileData[] | Date[]; type AnyJson = boolean | number | string | JsonFileData | Date | JsonArray | JsonArray[]; @@ -10,10 +15,33 @@ export interface JsonFileData { } export class JsonFile extends StdFile { - protected data?: JsonFileData; + protected data?: JsonFileData = undefined; + + constructor( + protected readonly cliService: CliService, + protected readonly directoryService: DirectoryService, + protected readonly fileService: FileService, + protected readonly fileDiffService: FileDiffService, + protected readonly fileFactory: FileFactory, + protected readonly file: string | null = null, + protected readonly encoding: BufferEncoding = "utf8", + content = "" + ) { + super( + cliService, + directoryService, + fileService, + fileDiffService, + fileFactory, + file, + encoding, + content + ); + this.setDataFromContent(); + } getContent(): string { - return JSON.stringify(this.data, null, " "); + return super.getContent(); } appendContent(content: string): this { @@ -44,8 +72,8 @@ export class JsonFile extends StdFile { return data[property]; } - protected parseContent(content: string): string { - content = super.parseContent(content); + protected setDataFromContent(): this { + const content = this.getContent(); try { this.data = JSON.parse(content); } catch (error) { @@ -55,6 +83,12 @@ export class JsonFile extends StdFile { )} => "${content.trim()}"` ); } - return content; + return this; + } + + protected setContent(content: string): this { + super.setContent(content); + this.setDataFromContent(); + return this; } } diff --git a/src/services/file/TomlFile.ts b/src/services/file/TomlFile.ts index 97e3c436..657c3f91 100644 --- a/src/services/file/TomlFile.ts +++ b/src/services/file/TomlFile.ts @@ -2,14 +2,36 @@ import { AnyJson, JsonMap, parse, stringify } from "@iarna/toml"; import { all } from "deepmerge"; import { StdFile } from "./StdFile"; +import { CliService } from "../CliService"; +import { DirectoryService } from "./DirectoryService"; +import { FileDiffService } from "./FileDiffService"; +import { FileFactory } from "./FileFactory"; +import { FileService } from "./FileService"; export class TomlFile extends StdFile { - protected data?: JsonMap; + protected data?: JsonMap = undefined; - protected parseContent(content: string): string { - content = super.parseContent(content); - this.data = parse(content); - return content; + constructor( + protected readonly cliService: CliService, + protected readonly directoryService: DirectoryService, + protected readonly fileService: FileService, + protected readonly fileDiffService: FileDiffService, + protected readonly fileFactory: FileFactory, + protected readonly file: string | null = null, + protected readonly encoding: BufferEncoding = "utf8", + content = "" + ) { + super( + cliService, + directoryService, + fileService, + fileDiffService, + fileFactory, + file, + encoding, + content + ); + this.setDataFromContent(); } getContent(): string { @@ -29,11 +51,32 @@ export class TomlFile extends StdFile { return this.setContent(stringify(newData)); } - getData(property?: undefined): JsonMap | undefined; - getData(property?: string): AnyJson | undefined { + getData(): JsonMap | undefined; + getData(property: string | undefined): AnyJson | undefined; + getData(property: string | undefined = undefined): AnyJson | undefined { if (!this.data) { return this.data; } return property ? this.data[property] : this.data; } + + protected setDataFromContent(): this { + const content = this.getContent(); + try { + this.data = parse(content); + } catch (error) { + throw new Error( + `An error occurred while parsing file content "${this.file}": ${JSON.stringify( + error instanceof Error ? error.message : error + )} => "${content.trim()}"` + ); + } + return this; + } + + protected setContent(content: string): this { + super.setContent(content); + this.setDataFromContent(); + return this; + } } diff --git a/src/services/file/TypescriptFile.ts b/src/services/file/TypescriptFile.ts index 70adbed3..d5267aad 100644 --- a/src/services/file/TypescriptFile.ts +++ b/src/services/file/TypescriptFile.ts @@ -15,29 +15,38 @@ export class TypescriptFile extends StdFile { protected declarations?: Array; protected defaultDeclaration?: string | null; - protected parseContent(content: string): string { - this.imports = []; - this.declarations = []; - this.defaultDeclaration = null; - - content = super.parseContent(content); - const body = this.parseTypescriptContent(content); - - for (const bodyItem of body) { - switch (bodyItem.kind) { - case SyntaxKind.ImportDeclaration: - this.parseImportDeclaration(bodyItem as ImportDeclaration); - break; - default: - this.declarations.push(content.substr(bodyItem.pos, bodyItem.end - bodyItem.pos)); - } - } - + protected setDataFromContent(): this { + const content = this.getContent(); try { - return this.getContent(); + this.imports = []; + this.declarations = []; + this.defaultDeclaration = null; + + const body = this.parseTypescriptContent(content); + + for (const bodyItem of body) { + switch (bodyItem.kind) { + case SyntaxKind.ImportDeclaration: + this.parseImportDeclaration(bodyItem as ImportDeclaration); + break; + default: + this.declarations.push(content.substr(bodyItem.pos, bodyItem.end - bodyItem.pos)); + } + } } catch (error) { - throw new Error('An error occurred while parsing content "' + content + '", ' + error); + throw new Error( + `An error occurred while parsing file content "${this.file}": ${JSON.stringify( + error instanceof Error ? error.message : error + )} => "${content.trim()}"` + ); } + return this; + } + + protected setContent(content: string): this { + super.setContent(content); + this.setDataFromContent(); + return this; } protected parseTypescriptContent(content: string): NodeArray { diff --git a/src/services/template/adapters/EtaAdapter.ts b/src/services/template/adapters/EtaAdapter.ts index 299a4588..c7f8663c 100644 --- a/src/services/template/adapters/EtaAdapter.ts +++ b/src/services/template/adapters/EtaAdapter.ts @@ -10,20 +10,22 @@ import { TemplateAdapter } from "./TemplateAdapter"; import { TemplateAdapterHelper } from "./TemplateAdapterHelper"; export class EtaAdapter implements TemplateAdapter { - private readonly eta: Eta = new Eta({ - views: this.templateFileService.getTemplateDirectory(), - debug: true, - cache: true, // Make Eta cache templates - autoEscape: false, // Not automatically XML-escape interpolations - autoTrim: false, // automatic whitespace trimming, - }); + private readonly eta: Eta; private readonly compiledTemplates: Map = new Map(); constructor( @inject(TemplateFileService) private readonly templateFileService: TemplateFileService, @inject(TemplateAdapterHelper) private readonly templateAdapterHelper: TemplateAdapterHelper - ) {} + ) { + this.eta = new Eta({ + views: this.templateFileService.getTemplateDirectory(), + debug: true, + cache: true, // Make Eta cache templates + autoEscape: false, // Not automatically XML-escape interpolations + autoTrim: false, // automatic whitespace trimming, + }); + } async renderTemplateString(template: string, context: TemplateContext): Promise { const compiledTemplate = await this.getCompiledTemplateString(template, template); diff --git a/tsconfig.json b/tsconfig.json index 13c06c71..6285bc53 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,7 @@ "noLib": false, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "target": "es2018", + "target": "es2022", "sourceMap": true, "strict": true, "outDir": "./dist",