@exanderal/stackcraft 0.4.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +90 -9
  2. package/dist/create/config.d.ts +13 -0
  3. package/dist/create/config.d.ts.map +1 -0
  4. package/dist/create/config.js +50 -0
  5. package/dist/create/config.js.map +1 -0
  6. package/dist/create/index.d.ts +6 -1
  7. package/dist/create/index.d.ts.map +1 -1
  8. package/dist/create/index.js +56 -20
  9. package/dist/create/index.js.map +1 -1
  10. package/dist/create/scaffolders/__tests__/web-vite.test.js +1 -0
  11. package/dist/create/scaffolders/__tests__/web-vite.test.js.map +1 -1
  12. package/dist/create/scaffolders/api-nestjs-graphql.d.ts.map +1 -1
  13. package/dist/create/scaffolders/api-nestjs-graphql.js +14 -17
  14. package/dist/create/scaffolders/api-nestjs-graphql.js.map +1 -1
  15. package/dist/create/scaffolders/api-nestjs-rest.d.ts.map +1 -1
  16. package/dist/create/scaffolders/api-nestjs-rest.js +14 -17
  17. package/dist/create/scaffolders/api-nestjs-rest.js.map +1 -1
  18. package/dist/create/scaffolders/orm.d.ts +6 -0
  19. package/dist/create/scaffolders/orm.d.ts.map +1 -0
  20. package/dist/create/scaffolders/orm.js +271 -0
  21. package/dist/create/scaffolders/orm.js.map +1 -0
  22. package/dist/create/scaffolders/setup-docker.js +33 -6
  23. package/dist/create/scaffolders/setup-docker.js.map +1 -1
  24. package/dist/create/scaffolders/utils/copy.d.ts.map +1 -1
  25. package/dist/create/scaffolders/utils/copy.js +1 -0
  26. package/dist/create/scaffolders/utils/copy.js.map +1 -1
  27. package/dist/create/types.d.ts +2 -0
  28. package/dist/create/types.d.ts.map +1 -1
  29. package/dist/index.js +8 -1
  30. package/dist/index.js.map +1 -1
  31. package/dist/init/index.d.ts +2 -0
  32. package/dist/init/index.d.ts.map +1 -0
  33. package/dist/init/index.js +38 -0
  34. package/dist/init/index.js.map +1 -0
  35. package/package.json +3 -2
  36. package/schema.json +60 -0
  37. package/templates/api-nestjs-graphql/package.json +1 -3
  38. package/templates/api-nestjs-rest/package.json +1 -3
  39. package/templates/base/tools/generators/module/graphql-files/__fileName__.model.ts__tmpl__ +10 -8
  40. package/templates/base/tools/generators/module/index.js +0 -4
  41. package/templates/base/tools/generators/module/schema.json +0 -6
  42. package/templates/orm-kysely/scripts/migration-create.ts +30 -0
  43. package/templates/orm-kysely/src/common/repositories/entity.repository.ts +33 -0
  44. package/templates/orm-kysely/src/common/repositories/readonly-entity.repository.ts +46 -0
  45. package/templates/orm-kysely/src/database/database.types.ts +17 -0
  46. package/templates/orm-kysely/src/database/migrations/.gitkeep +0 -0
  47. package/templates/orm-kysely/src/database/seeds/seed.ts +12 -0
  48. package/templates/orm-kysely/src/database/utils/columns.ts +32 -0
  49. package/templates/orm-kysely/src/database/utils/index.ts +3 -0
  50. package/templates/orm-kysely/src/database/utils/indexes.ts +20 -0
  51. package/templates/orm-kysely/src/database/utils/tables.ts +7 -0
  52. package/templates/orm-kysely/src/modules/database/database.module.ts +9 -0
  53. package/templates/orm-kysely/tools/generators/module/files/__fileName__.model.ts__tmpl__ +16 -0
  54. package/templates/orm-kysely/tools/generators/module/files/__fileName__.module.ts__tmpl__ +9 -0
  55. package/templates/orm-kysely/tools/generators/module/files/__fileName__.repository.ts__tmpl__ +26 -0
  56. package/templates/orm-kysely/tools/generators/module/files/__fileName__.service.ts__tmpl__ +28 -0
  57. package/templates/orm-kysely/tools/generators/module/files/__tests__/__fileName__.integration.spec.ts__tmpl__ +23 -0
  58. package/templates/orm-kysely/tools/generators/module/graphql-files/__fileName__.model.ts__tmpl__ +24 -0
  59. package/templates/orm-prisma/prisma/schema.prisma +8 -0
  60. package/templates/orm-prisma/prisma/seeds/seed.ts +16 -0
  61. package/templates/orm-prisma/src/modules/database/database.module.ts +8 -0
  62. package/templates/orm-prisma/src/modules/prisma/prisma.module.ts +9 -0
  63. package/templates/orm-prisma/src/modules/prisma/prisma.service.ts +9 -0
  64. package/templates/orm-prisma/tools/generators/module/files/__fileName__.model.ts__tmpl__ +15 -0
  65. package/templates/orm-prisma/tools/generators/module/files/__fileName__.module.ts__tmpl__ +9 -0
  66. package/templates/orm-prisma/tools/generators/module/files/__fileName__.repository.ts__tmpl__ +28 -0
  67. package/templates/orm-prisma/tools/generators/module/files/__fileName__.service.ts__tmpl__ +28 -0
  68. package/templates/orm-prisma/tools/generators/module/files/__tests__/__fileName__.integration.spec.ts__tmpl__ +23 -0
  69. package/templates/orm-prisma/tools/generators/module/graphql-files/__fileName__.model.ts__tmpl__ +23 -0
  70. package/templates/api-nestjs-graphql/src/common/entities/base.entity.ts +0 -21
  71. package/templates/api-nestjs-graphql/src/common/repositories/entity.repository.ts +0 -21
  72. package/templates/api-nestjs-graphql/src/common/repositories/readonly-entity.repository.ts +0 -22
  73. package/templates/api-nestjs-graphql/src/common/services/entity.service.ts +0 -24
  74. package/templates/api-nestjs-graphql/src/common/services/readonly-entity.service.ts +0 -23
  75. package/templates/api-nestjs-graphql/src/modules/database/database.module.ts +0 -18
  76. package/templates/api-nestjs-rest/src/common/entities/base.entity.ts +0 -16
  77. package/templates/api-nestjs-rest/src/common/repositories/entity.repository.ts +0 -21
  78. package/templates/api-nestjs-rest/src/common/repositories/readonly-entity.repository.ts +0 -22
  79. package/templates/api-nestjs-rest/src/common/services/entity.service.ts +0 -24
  80. package/templates/api-nestjs-rest/src/common/services/readonly-entity.service.ts +0 -23
  81. package/templates/api-nestjs-rest/src/modules/database/database.module.ts +0 -18
@@ -0,0 +1,30 @@
1
+ import { writeFileSync } from 'node:fs';
2
+
3
+ const name = process.argv[2];
4
+ if (!name) {
5
+ console.error('Usage: pnpm migration:new <name>');
6
+ process.exit(1);
7
+ }
8
+
9
+ const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
10
+ const path = `src/database/migrations/${ts}-${name}.ts`;
11
+
12
+ writeFileSync(
13
+ path,
14
+ `import { Kysely } from 'kysely';
15
+ import { withDefaults, createTable, dropTable } from '../utils';
16
+
17
+ export async function up(db: Kysely<any>): Promise<void> {
18
+ await withDefaults(
19
+ createTable(db, '${name}')
20
+ // .addColumn('name', 'varchar(255)', (col) => col.notNull())
21
+ ).execute();
22
+ }
23
+
24
+ export async function down(db: Kysely<any>): Promise<void> {
25
+ await dropTable(db, '${name}');
26
+ }
27
+ `,
28
+ );
29
+
30
+ console.log('Created:', path);
@@ -0,0 +1,33 @@
1
+ import { ReadonlyEntityRepository } from './readonly-entity.repository';
2
+
3
+ export abstract class EntityRepository<
4
+ TEntity,
5
+ TFilters extends Record<string, unknown> = Record<string, never>,
6
+ TInsert = Omit<TEntity, 'id' | 'createdAt' | 'updatedAt'>,
7
+ > extends ReadonlyEntityRepository<TEntity, TFilters> {
8
+ async create(data: TInsert): Promise<TEntity> {
9
+ const row = await this.db
10
+ .insertInto(this.table as any)
11
+ .values(data as any)
12
+ .returningAll()
13
+ .executeTakeFirstOrThrow();
14
+ return this.mapFromDB(row as any);
15
+ }
16
+
17
+ async update(id: string | number, data: Partial<TInsert>): Promise<TEntity> {
18
+ const row = await this.db
19
+ .updateTable(this.table as any)
20
+ .set(data as any)
21
+ .where(`${this.table}.id` as any, '=', id)
22
+ .returningAll()
23
+ .executeTakeFirstOrThrow();
24
+ return this.mapFromDB(row as any);
25
+ }
26
+
27
+ async remove(id: string | number): Promise<void> {
28
+ await this.db
29
+ .deleteFrom(this.table as any)
30
+ .where(`${this.table}.id` as any, '=', id)
31
+ .execute();
32
+ }
33
+ }
@@ -0,0 +1,46 @@
1
+ import { Kysely } from 'kysely';
2
+ import type { Database } from '../../database/database.types';
3
+
4
+ export function where(column: string) {
5
+ return (qb: any, value: unknown) => qb.where(column, '=', value);
6
+ }
7
+
8
+ export abstract class ReadonlyEntityRepository<
9
+ TEntity,
10
+ TFilters extends Record<string, unknown> = Record<string, never>,
11
+ > {
12
+ constructor(protected readonly db: Kysely<Database>) {}
13
+
14
+ protected abstract table: string;
15
+
16
+ protected filters: Partial<
17
+ Record<keyof TFilters, (qb: any, value: any) => any>
18
+ > = {};
19
+
20
+ protected queryable() {
21
+ return this.db.selectFrom(this.table as any);
22
+ }
23
+
24
+ async findAll(filters?: Partial<TFilters>): Promise<TEntity[]> {
25
+ let q = this.queryable();
26
+ if (filters) {
27
+ for (const [key, value] of Object.entries(filters)) {
28
+ const fn = (this.filters as any)[key];
29
+ if (value !== undefined && fn) q = fn(q, value);
30
+ }
31
+ }
32
+ return (await q.selectAll().execute()).map((r) => this.mapFromDB(r as any));
33
+ }
34
+
35
+ async findById(id: string | number): Promise<TEntity | null> {
36
+ const row = await this.queryable()
37
+ .selectAll()
38
+ .where(`${this.table}.id` as any, '=', id)
39
+ .executeTakeFirst();
40
+ return row ? this.mapFromDB(row as any) : null;
41
+ }
42
+
43
+ protected mapFromDB(row: any): TEntity {
44
+ return row;
45
+ }
46
+ }
@@ -0,0 +1,17 @@
1
+ // Add a key per table as you create migrations.
2
+ // Example:
3
+ // import { Generated, Selectable, Insertable, Updateable } from 'kysely'
4
+ //
5
+ // export interface UsersTable {
6
+ // id: Generated<string>
7
+ // email: string
8
+ // name: string
9
+ // created_at: Generated<Date>
10
+ // updated_at: Generated<Date>
11
+ // }
12
+ //
13
+ // export interface Database {
14
+ // users: UsersTable
15
+ // }
16
+
17
+ export interface Database {}
@@ -0,0 +1,12 @@
1
+ import 'dotenv/config';
2
+
3
+ async function seed() {
4
+ // Add seed data here.
5
+ // Example: connect to the DB and insert rows.
6
+ console.log('Seed complete.');
7
+ }
8
+
9
+ seed().catch((e) => {
10
+ console.error(e);
11
+ process.exit(1);
12
+ });
@@ -0,0 +1,32 @@
1
+ import { sql, CreateTableBuilder } from 'kysely';
2
+
3
+ export function withUuid<T extends string, C extends string>(
4
+ builder: CreateTableBuilder<T, C>,
5
+ dialect: 'postgres' | 'mysql' = 'postgres',
6
+ ) {
7
+ const defaultFn =
8
+ dialect === 'mysql' ? sql`(UUID())` : sql`gen_random_uuid()`;
9
+ return builder.addColumn('id', 'uuid', (col) =>
10
+ col.primaryKey().defaultTo(defaultFn),
11
+ );
12
+ }
13
+
14
+ export function withTimestamps<T extends string, C extends string>(
15
+ builder: CreateTableBuilder<T, C>,
16
+ ) {
17
+ return builder
18
+ .addColumn('created_at', 'timestamptz', (col) =>
19
+ col.notNull().defaultTo(sql`now()`),
20
+ )
21
+ .addColumn('updated_at', 'timestamptz', (col) =>
22
+ col.notNull().defaultTo(sql`now()`),
23
+ );
24
+ }
25
+
26
+ // Shorthand: uuid primary key + timestamps
27
+ export function withDefaults<T extends string, C extends string>(
28
+ builder: CreateTableBuilder<T, C>,
29
+ dialect: 'postgres' | 'mysql' = 'postgres',
30
+ ) {
31
+ return withTimestamps(withUuid(builder, dialect));
32
+ }
@@ -0,0 +1,3 @@
1
+ export * from './columns';
2
+ export * from './indexes';
3
+ export * from './tables';
@@ -0,0 +1,20 @@
1
+ import { Kysely } from 'kysely';
2
+
3
+ export async function createIndex(
4
+ db: Kysely<any>,
5
+ table: string,
6
+ columns: string | string[],
7
+ opts?: { unique?: boolean; name?: string },
8
+ ) {
9
+ const cols = Array.isArray(columns) ? columns : [columns];
10
+ const name = opts?.name ?? `idx_${table}_${cols.join('_')}`;
11
+ let b = db.schema.createIndex(name).on(table).columns(cols);
12
+ if (opts?.unique) b = (b as any).unique();
13
+ await b.execute();
14
+ }
15
+
16
+ export const createUniqueIndex = (
17
+ db: Kysely<any>,
18
+ table: string,
19
+ columns: string | string[],
20
+ ) => createIndex(db, table, columns, { unique: true });
@@ -0,0 +1,7 @@
1
+ import { Kysely } from 'kysely';
2
+
3
+ export const createTable = (db: Kysely<any>, name: string) =>
4
+ db.schema.createTable(name);
5
+
6
+ export const dropTable = (db: Kysely<any>, name: string) =>
7
+ db.schema.dropTable(name).ifExists().execute();
@@ -0,0 +1,9 @@
1
+ import { Global, Module } from '@nestjs/common';
2
+ import { KyselyService } from './kysely.service';
3
+
4
+ @Global()
5
+ @Module({
6
+ providers: [KyselyService],
7
+ exports: [KyselyService],
8
+ })
9
+ export class DatabaseModule {}
@@ -0,0 +1,16 @@
1
+ // Define this table in src/database/database.types.ts:
2
+ //
3
+ // export interface <%= className %>sTable {
4
+ // id: Generated<string>
5
+ // created_at: Generated<Date>
6
+ // updated_at: Generated<Date>
7
+ // }
8
+ //
9
+ // Then add to Database interface:
10
+ // <%= fileName %>s: <%= className %>sTable
11
+
12
+ export interface <%= className %> {
13
+ id: string;
14
+ createdAt: Date;
15
+ updatedAt: Date;
16
+ }
@@ -0,0 +1,9 @@
1
+ import { Module } from '@nestjs/common';
2
+ import { <%= className %>Repository } from './<%= fileName %>.repository';
3
+ import { <%= className %>Service } from './<%= fileName %>.service';
4
+
5
+ @Module({
6
+ providers: [<%= className %>Service, <%= className %>Repository],
7
+ exports: [<%= className %>Service, <%= className %>Repository],
8
+ })
9
+ export class <%= className %>Module {}
@@ -0,0 +1,26 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { EntityRepository } from '../../common/repositories/entity.repository';
3
+ import { KyselyService } from '../../modules/database/kysely.service';
4
+ import type { <%= className %> } from './<%= fileName %>.model';
5
+
6
+ // Add properties to enable filtering, e.g.:
7
+ // export type <%= className %>Filters = { id?: string }
8
+ export type <%= className %>Filters = Record<string, never>
9
+
10
+ @Injectable()
11
+ export class <%= className %>Repository extends EntityRepository<<%= className %>, <%= className %>Filters> {
12
+ protected table = '<%= fileName %>s';
13
+
14
+ constructor(kyselyService: KyselyService) {
15
+ super(kyselyService.db);
16
+ }
17
+
18
+ // protected filters = {
19
+ // id: where('<%= fileName %>s.id'),
20
+ // };
21
+
22
+ // protected override queryable() {
23
+ // return super.queryable()
24
+ // .innerJoin('other_table', 'other_table.id', '<%= fileName %>s.other_id')
25
+ // }
26
+ }
@@ -0,0 +1,28 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import type { <%= className %> } from './<%= fileName %>.model';
3
+ import { <%= className %>Repository } from './<%= fileName %>.repository';
4
+
5
+ @Injectable()
6
+ export class <%= className %>Service {
7
+ constructor(private readonly repository: <%= className %>Repository) {}
8
+
9
+ findAll() {
10
+ return this.repository.findAll();
11
+ }
12
+
13
+ findById(id: string) {
14
+ return this.repository.findById(id);
15
+ }
16
+
17
+ create(data: Omit<<%= className %>, 'id' | 'createdAt' | 'updatedAt'>) {
18
+ return this.repository.create(data);
19
+ }
20
+
21
+ update(id: string, data: Partial<Omit<<%= className %>, 'id' | 'createdAt' | 'updatedAt'>>) {
22
+ return this.repository.update(id, data);
23
+ }
24
+
25
+ remove(id: string) {
26
+ return this.repository.remove(id);
27
+ }
28
+ }
@@ -0,0 +1,23 @@
1
+ import { Test } from '@nestjs/testing';
2
+ import { KyselyService } from '../../modules/database/kysely.service';
3
+ import { <%= className %>Module } from '../<%= fileName %>.module';
4
+ import { <%= className %>Service } from '../<%= fileName %>.service';
5
+
6
+ describe('<%= className %>Service', () => {
7
+ let service: <%= className %>Service;
8
+
9
+ beforeAll(async () => {
10
+ const module = await Test.createTestingModule({
11
+ imports: [<%= className %>Module],
12
+ })
13
+ .overrideProvider(KyselyService)
14
+ .useValue({ db: {} })
15
+ .compile();
16
+
17
+ service = module.get(<%= className %>Service);
18
+ });
19
+
20
+ it('is defined', () => {
21
+ expect(service).toBeDefined();
22
+ });
23
+ });
@@ -0,0 +1,24 @@
1
+ import { Field, ID, ObjectType } from '@nestjs/graphql';
2
+
3
+ // Define this table in src/database/database.types.ts:
4
+ //
5
+ // export interface <%= className %>sTable {
6
+ // id: Generated<string>
7
+ // created_at: Generated<Date>
8
+ // updated_at: Generated<Date>
9
+ // }
10
+ //
11
+ // Then add to Database interface:
12
+ // <%= fileName %>s: <%= className %>sTable
13
+
14
+ @ObjectType()
15
+ export class <%= className %> {
16
+ @Field(() => ID)
17
+ id: string;
18
+
19
+ @Field(() => String)
20
+ createdAt: Date;
21
+
22
+ @Field(() => String)
23
+ updatedAt: Date;
24
+ }
@@ -0,0 +1,8 @@
1
+ generator client {
2
+ provider = "prisma-client-js"
3
+ }
4
+
5
+ datasource db {
6
+ provider = "{{dbProvider}}"
7
+ url = env("DATABASE_URL")
8
+ }
@@ -0,0 +1,16 @@
1
+ import { PrismaClient } from '@prisma/client';
2
+
3
+ const prisma = new PrismaClient();
4
+
5
+ async function main() {
6
+ // Example:
7
+ // await prisma.user.create({ data: { email: 'admin@example.com', name: 'Admin' } });
8
+ console.log('Seed complete.');
9
+ }
10
+
11
+ main()
12
+ .catch((e) => {
13
+ console.error(e);
14
+ process.exit(1);
15
+ })
16
+ .finally(() => prisma.$disconnect());
@@ -0,0 +1,8 @@
1
+ import { Module } from '@nestjs/common';
2
+ import { PrismaModule } from '../prisma/prisma.module';
3
+
4
+ @Module({
5
+ imports: [PrismaModule],
6
+ exports: [PrismaModule],
7
+ })
8
+ export class DatabaseModule {}
@@ -0,0 +1,9 @@
1
+ import { Global, Module } from '@nestjs/common';
2
+ import { PrismaService } from './prisma.service';
3
+
4
+ @Global()
5
+ @Module({
6
+ providers: [PrismaService],
7
+ exports: [PrismaService],
8
+ })
9
+ export class PrismaModule {}
@@ -0,0 +1,9 @@
1
+ import { Injectable, OnModuleInit } from '@nestjs/common';
2
+ import { PrismaClient } from '@prisma/client';
3
+
4
+ @Injectable()
5
+ export class PrismaService extends PrismaClient implements OnModuleInit {
6
+ async onModuleInit() {
7
+ await this.$connect();
8
+ }
9
+ }
@@ -0,0 +1,15 @@
1
+ // Add this model to prisma/schema.prisma:
2
+ //
3
+ // model <%= className %> {
4
+ // id String @id @default(cuid())
5
+ // createdAt DateTime @default(now())
6
+ // updatedAt DateTime @updatedAt
7
+ // }
8
+ //
9
+ // Then run: pnpm migration:run
10
+
11
+ export type <%= className %> = {
12
+ id: string;
13
+ createdAt: Date;
14
+ updatedAt: Date;
15
+ };
@@ -0,0 +1,9 @@
1
+ import { Module } from '@nestjs/common';
2
+ import { <%= className %>Repository } from './<%= fileName %>.repository';
3
+ import { <%= className %>Service } from './<%= fileName %>.service';
4
+
5
+ @Module({
6
+ providers: [<%= className %>Service, <%= className %>Repository],
7
+ exports: [<%= className %>Service, <%= className %>Repository],
8
+ })
9
+ export class <%= className %>Module {}
@@ -0,0 +1,28 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { PrismaService } from '../../modules/prisma/prisma.service';
3
+ import type { <%= className %> } from './<%= fileName %>.model';
4
+
5
+ @Injectable()
6
+ export class <%= className %>Repository {
7
+ constructor(private readonly prisma: PrismaService) {}
8
+
9
+ findAll() {
10
+ return this.prisma.<%= fileName %>.findMany();
11
+ }
12
+
13
+ findById(id: string) {
14
+ return this.prisma.<%= fileName %>.findUnique({ where: { id } });
15
+ }
16
+
17
+ create(data: Omit<<%= className %>, 'id' | 'createdAt' | 'updatedAt'>) {
18
+ return this.prisma.<%= fileName %>.create({ data });
19
+ }
20
+
21
+ update(id: string, data: Partial<Omit<<%= className %>, 'id' | 'createdAt' | 'updatedAt'>>) {
22
+ return this.prisma.<%= fileName %>.update({ where: { id }, data });
23
+ }
24
+
25
+ remove(id: string) {
26
+ return this.prisma.<%= fileName %>.delete({ where: { id } });
27
+ }
28
+ }
@@ -0,0 +1,28 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import type { <%= className %> } from './<%= fileName %>.model';
3
+ import { <%= className %>Repository } from './<%= fileName %>.repository';
4
+
5
+ @Injectable()
6
+ export class <%= className %>Service {
7
+ constructor(private readonly repository: <%= className %>Repository) {}
8
+
9
+ findAll(): Promise<<%= className %>[]> {
10
+ return this.repository.findAll();
11
+ }
12
+
13
+ findById(id: string): Promise<<%= className %> | null> {
14
+ return this.repository.findById(id);
15
+ }
16
+
17
+ create(data: Omit<<%= className %>, 'id' | 'createdAt' | 'updatedAt'>): Promise<<%= className %>> {
18
+ return this.repository.create(data);
19
+ }
20
+
21
+ update(id: string, data: Partial<Omit<<%= className %>, 'id' | 'createdAt' | 'updatedAt'>>): Promise<<%= className %>> {
22
+ return this.repository.update(id, data);
23
+ }
24
+
25
+ remove(id: string): Promise<<%= className %>> {
26
+ return this.repository.remove(id);
27
+ }
28
+ }
@@ -0,0 +1,23 @@
1
+ import { Test } from '@nestjs/testing';
2
+ import { PrismaService } from '../../modules/prisma/prisma.service';
3
+ import { <%= className %>Module } from '../<%= fileName %>.module';
4
+ import { <%= className %>Service } from '../<%= fileName %>.service';
5
+
6
+ describe('<%= className %>Service', () => {
7
+ let service: <%= className %>Service;
8
+
9
+ beforeAll(async () => {
10
+ const module = await Test.createTestingModule({
11
+ imports: [<%= className %>Module],
12
+ })
13
+ .overrideProvider(PrismaService)
14
+ .useValue({})
15
+ .compile();
16
+
17
+ service = module.get(<%= className %>Service);
18
+ });
19
+
20
+ it('is defined', () => {
21
+ expect(service).toBeDefined();
22
+ });
23
+ });
@@ -0,0 +1,23 @@
1
+ import { Field, ID, ObjectType } from '@nestjs/graphql';
2
+
3
+ // Add this model to prisma/schema.prisma:
4
+ //
5
+ // model <%= className %> {
6
+ // id String @id @default(cuid())
7
+ // createdAt DateTime @default(now())
8
+ // updatedAt DateTime @updatedAt
9
+ // }
10
+ //
11
+ // Then run: pnpm migration:run
12
+
13
+ @ObjectType()
14
+ export class <%= className %> {
15
+ @Field(() => ID)
16
+ id: string;
17
+
18
+ @Field(() => String)
19
+ createdAt: Date;
20
+
21
+ @Field(() => String)
22
+ updatedAt: Date;
23
+ }
@@ -1,21 +0,0 @@
1
- import { Field, ID, ObjectType } from '@nestjs/graphql';
2
- import {
3
- CreateDateColumn,
4
- PrimaryGeneratedColumn,
5
- UpdateDateColumn,
6
- } from 'typeorm';
7
-
8
- @ObjectType({ isAbstract: true })
9
- export abstract class BaseEntity {
10
- @Field(() => ID)
11
- @PrimaryGeneratedColumn('uuid')
12
- id: string;
13
-
14
- @Field()
15
- @CreateDateColumn()
16
- createdAt: Date;
17
-
18
- @Field()
19
- @UpdateDateColumn()
20
- updatedAt: Date;
21
- }
@@ -1,21 +0,0 @@
1
- import { DeepPartial } from 'typeorm';
2
- import { BaseEntity } from '../entities/base.entity';
3
- import { ReadonlyEntityRepository } from './readonly-entity.repository';
4
-
5
- export abstract class EntityRepository<
6
- T extends BaseEntity,
7
- > extends ReadonlyEntityRepository<T> {
8
- async create(data: DeepPartial<T>): Promise<T> {
9
- const entity = this.repo.create(data);
10
- return this.repo.save(entity);
11
- }
12
-
13
- async update(id: string, data: DeepPartial<T>): Promise<T> {
14
- await this.repo.update(id, data as any);
15
- return this.findById(id) as Promise<T>;
16
- }
17
-
18
- async remove(id: string): Promise<void> {
19
- await this.repo.delete(id);
20
- }
21
- }
@@ -1,22 +0,0 @@
1
- import { FindManyOptions, FindOptionsWhere, In, Repository } from 'typeorm';
2
- import { BaseEntity } from '../entities/base.entity';
3
-
4
- export abstract class ReadonlyEntityRepository<T extends BaseEntity> {
5
- constructor(protected readonly repo: Repository<T>) {}
6
-
7
- findAll(options?: FindManyOptions<T>): Promise<T[]> {
8
- return this.repo.find(options);
9
- }
10
-
11
- findByIds(ids: string[]): Promise<T[]> {
12
- return this.repo.findBy({ id: In(ids) } as FindOptionsWhere<T>);
13
- }
14
-
15
- findById(id: string): Promise<T | null> {
16
- return this.repo.findOneBy({ id } as FindOptionsWhere<T>);
17
- }
18
-
19
- findOne(where: FindOptionsWhere<T>): Promise<T | null> {
20
- return this.repo.findOneBy(where);
21
- }
22
- }
@@ -1,24 +0,0 @@
1
- import { DeepPartial } from 'typeorm';
2
- import { BaseEntity } from '../entities/base.entity';
3
- import { EntityRepository } from '../repositories/entity.repository';
4
- import { ReadonlyEntityService } from './readonly-entity.service';
5
-
6
- export abstract class EntityService<
7
- T extends BaseEntity,
8
- > extends ReadonlyEntityService<T> {
9
- constructor(protected readonly repository: EntityRepository<T>) {
10
- super(repository);
11
- }
12
-
13
- create(data: DeepPartial<T>): Promise<T> {
14
- return this.repository.create(data);
15
- }
16
-
17
- update(id: string, data: DeepPartial<T>): Promise<T> {
18
- return this.repository.update(id, data);
19
- }
20
-
21
- remove(id: string): Promise<void> {
22
- return this.repository.remove(id);
23
- }
24
- }