Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions __tests__/integration/layers.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import fsPromises from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { defaultLayers } from "@/constants";
import { basePath as defaultBasePath, defaultLayers } from "@/constants";
import { createLayersIfNotExists } from "@/layers";

describe("Layers folders structure", () => {
const defaultMainDirectory = "src";

let basePath: string;
let basePath: string = defaultBasePath;

async function getFolders(...paths: string[]) {
return await fsPromises.readdir(join(...paths));
Expand Down
43 changes: 16 additions & 27 deletions __tests__/unit/templates.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import templates from "@/templates";
import { Kebab, Pascal } from "@/utils/formats";
import { template } from "@/utils/tags";
import {
factoryTemplateMock,
Expand All @@ -15,9 +16,9 @@ describe("Code generation", () => {

it("should generate a repository template", () => {
const expected: Component = {
filename: template`${{ value: resource, casing: "kebab" }}.repository.ts`,
filename: template`${new Kebab(resource)}.repository.ts`(),
body: repositoryTemplateMock,
name: template`${{ value: resource, casing: "pascal" }}Repository`,
name: template`${new Pascal(resource)}Repository`(),
};

const result = templates.get("repository")?.(resource);
Expand All @@ -28,53 +29,41 @@ describe("Code generation", () => {

it("should generate a service template", () => {
const expected: Component = {
filename: template`${{ value: resource, casing: "kebab" }}.service.ts`,
filename: template`${new Kebab(resource)}.service.ts`(),
body: serviceTemplateMock,
name: template`${{ value: resource, casing: "pascal" }}Service`,
name: template`${new Pascal(resource)}Service`(),
};

const result = templates.get("service")?.(resource);

expect(result).toEqual(expected);
expect(result?.body).toContain(
template`import ${{
value: resource,
casing: "pascal",
}}Repository from "../repository/${{
value: resource,
casing: "kebab",
}}.repository";`,
template`import ${new Pascal(
resource,
)}Repository from "../repository/${new Kebab(resource)}.repository";`(),
);
expect(result?.body).toContain(`export default class ${result?.name}`);
});

it("should generate a factory template", () => {
const expected: Component = {
filename: template`${{ value: resource, casing: "kebab" }}.factory.ts`,
filename: template`${new Kebab(resource)}.factory.ts`(),
body: factoryTemplateMock,
name: template`${{ value: resource, casing: "pascal" }}Factory`,
name: template`${new Pascal(resource)}Factory`(),
};

const result = templates.get("factory")?.(resource);

expect(result).toEqual(expected);
expect(result?.body).toContain(
template`import ${{
value: resource,
casing: "pascal",
}}Repository from "../repository/${{
value: resource,
casing: "kebab",
}}.repository";`,
template`import ${new Pascal(
resource,
)}Repository from "../repository/${new Kebab(resource)}.repository";`(),
);
expect(result?.body).toContain(
template`import ${{
value: resource,
casing: "pascal",
}}Service from "../service/${{
value: resource,
casing: "kebab",
}}.service";`,
template`import ${new Pascal(
resource,
)}Service from "../service/${new Kebab(resource)}.service";`(),
);
expect(result?.body).toContain(`export default class ${result?.name}`);
});
Expand Down
64 changes: 64 additions & 0 deletions __tests__/unit/utils/formats.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Camel, Kebab, Lower, Pascal, Snake, Upper } from "@/utils/formats";

describe("formats", () => {
const cases = [
{
Class: Upper,
input: "hello world",
expected: "HELLO WORLD",
},
{
Class: Lower,
input: "HELLO WORLD",
expected: "hello world",
},
{
Class: Camel,
input: "hello world",
expected: "helloWorld",
},
{
Class: Pascal,
input: "hello world",
expected: "HelloWorld",
},
{
Class: Kebab,
input: "hello world",
expected: "hello-world",
},
{
Class: Snake,
input: "hello world",
expected: "hello_world",
},
];

it.each(cases)(
"$Class.name should format '$input' correctly",
({ Class, input, expected }) => {
const instance = new Class(input);
expect(instance.toString()).toBe(expected);
},
);

it("should support symbol-toPrimitive for string coercion", () => {
const instance = new Pascal("hello world");
expect(`${instance}`).toBe("HelloWorld");
});

it("should handle multiple delimiters", () => {
const snake = new Snake("this--is__a test");
const kebab = new Kebab("this--is__a test");
const camel = new Camel("this--is__a test");

expect(snake.toString()).toBe("this_is_a_test");
expect(kebab.toString()).toBe("this-is-a-test");
expect(camel.toString()).toBe("thisIsATest");
});

it("should return empty string for undefined or null values", () => {
expect(new Upper(undefined).toString()).toBe("");
expect(new Kebab(null).toString()).toBe("");
});
});
60 changes: 42 additions & 18 deletions __tests__/unit/utils/tags.test.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,56 @@
import {
Camel,
type Case,
Kebab,
Lower,
Pascal,
Snake,
Upper,
} from "@/utils/formats";
import { template } from "@/utils/tags";

describe("tags", () => {
describe("template", () => {
it("should parse a placeholder based on the given values", () => {
for (const [casing, expected] of [
["camel", "camelCase"],
["pascal", "PascalCase"],
["kebab", "kebab-case"],
["snake", "snake_case"],
["upper", "UPPER CASE"],
["lower", "lower case"],
[undefined, "undefined case"],
[null, ""],
] as [Template.Casing, string][]) {
const result = template`${{
value: expected ? `${casing} case` : null,
casing,
}}`;
for (const [Format, expected] of [
[Camel, "camelCase"],
[Pascal, "PascalCase"],
[Kebab, "kebab-case"],
[Snake, "snake_case"],
[Upper, "UPPER CASE"],
[Lower, "lower case"],
] as [new (value: string) => Case, string][]) {
const result = template`${new Format(`${Format.name} case`)}`();
expect(result).toStrictEqual(expected);
}
});

it("should ignore falsy values", () => {
const result = template`${new Pascal(
"user",
)}${undefined}${null}Repository`();
expect(result).toStrictEqual("UserRepository");
});

it("should work as the example", () => {
const result = template`class ${{
value: "user",
casing: "pascal",
}}Repository {}`;
const result = template`class ${new Pascal("user")}Repository {}`();
expect(result).toStrictEqual("class UserRepository {}");
});

describe("compose", () => {
it("should compose the result with a function", () => {
const result = template`${new Pascal("user")}Repository`.compose(
(input) => `${input}Test`,
)();
expect(result).toStrictEqual("UserRepositoryTest");
});

it("should return the composed value when calling thenRender", () => {
const result = template`${new Pascal("user")}Repository`
.compose((input) => `${input}Test`)
.thenRender();
expect(result).toStrictEqual("UserRepositoryTest");
});
});
});
});
35 changes: 13 additions & 22 deletions src/templates/factory.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,26 @@
import { Kebab, Pascal } from "@/utils/formats";
import { template } from "@/utils/tags";

function body(resource: string) {
return template`import ${{
value: resource,
casing: "pascal",
}}Repository from "../repository/${{ value: resource, casing: "kebab" }}.repository";
import ${{ value: resource, casing: "pascal" }}Service from "../service/${{
value: resource,
casing: "kebab",
}}.service";
return template`import ${new Pascal(
resource,
)}Repository from "../repository/${new Kebab(resource)}.repository";
import ${new Pascal(resource)}Service from "../service/${new Kebab(resource)}.service";

export default class ${{ value: resource, casing: "pascal" }}Factory {
export default class ${new Pascal(resource)}Factory {
static getInstance() {
return new ${{ value: resource, casing: "pascal" }}Service(new ${{
value: resource,
casing: "pascal",
}}Repository());
return new ${new Pascal(resource)}Service(new ${new Pascal(resource)}Repository());
}
}
`;
}

function filename(
resource: string,
casing: Extract<Template.Casing, "kebab" | "camel"> = "kebab",
) {
return template`${{ value: resource, casing: casing }}.factory.ts`;
function filename(resource: string) {
return template`${new Kebab(resource)}.factory.ts`;
}

function name(resource: string) {
return template`${{ value: resource, casing: "pascal" }}Factory`;
return template`${new Pascal(resource)}Factory`;
}

/**
Expand All @@ -40,8 +31,8 @@ function name(resource: string) {
*/
export function component(resource: string): Component {
return {
name: name(resource),
filename: filename(resource),
body: body(resource),
name: name(resource).render(),
filename: filename(resource).render(),
body: body(resource).render(),
};
}
15 changes: 8 additions & 7 deletions src/templates/repository.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Kebab, Pascal } from "@/utils/formats";
import { template } from "@/utils/tags";

function body(resource: string) {
return template`export default class ${{ value: resource, casing: "pascal" }}Repository {
return template`export default class ${new Pascal(resource)}Repository {
constructor() {}

async create(data: any) {
Expand All @@ -23,12 +24,12 @@ function body(resource: string) {
`;
}

function filename(resource: string, casing: Template.Casing = "kebab") {
return template`${{ value: resource, casing: casing }}.repository.ts`;
function filename(resource: string) {
return template`${new Kebab(resource)}.repository.ts`;
}

function name(resource: string) {
return template`${{ value: resource, casing: "pascal" }}Repository`;
return template`${new Pascal(resource)}Repository`;
}

/**
Expand All @@ -39,8 +40,8 @@ function name(resource: string) {
*/
export function component(resource: string): Component {
return {
name: name(resource),
filename: filename(resource),
body: body(resource),
name: name(resource).render(),
filename: filename(resource).render(),
body: body(resource).render(),
};
}
Loading