Created
June 15, 2023 17:25
-
-
Save wgd3/e86c6ebcf72fdb9f19582c4192a2f376 to your computer and use it in GitHub Desktop.
Nx generator and template files used to create TypeORM entities, interfaces, services, and repositories.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { <%=className%>Service } from '@myapp/server/core/application-services'; | |
import { IRequestUserObject } from '@myapp/server/core/domain'; | |
import { I<%=className%>, RoleEnum } from '@myapp/shared/domain'; | |
import { | |
Body, | |
Controller, | |
Get, | |
Logger, | |
Param, | |
ParseUUIDPipe, | |
Post, | |
Query, | |
} from '@nestjs/common'; | |
import { ApiTags } from '@nestjs/swagger'; | |
import { ReqUser } from '../decorators/req-user.decorator'; | |
import { PaginationQueryDto } from '../dtos/query-pagination.dto'; | |
import { Create<%= className %>Dto } from '../dtos/create-<%= fileName %>.dto'; | |
@Controller({ path: '<%=className%>', version: '1' }) | |
@ApiTags('<%=className%>') | |
export class <%=className%>Controller { | |
private logger = new Logger(<%=className%>Controller.name); | |
constructor(private <%=propertyName%>Service: <%=className%>Service) {} | |
@Get('') | |
async getMany( | |
@ReqUser() user: IRequestUserObject, | |
@Query() searchOpts: unknown, | |
@Query() pagination: PaginationQueryDto<I<%=className%>> | |
): Promise<I<%=className%>[]> { | |
this.logger.log(`Returning <%=className%> for ${user.email}`); | |
return await this.<%=propertyName%>Service.getMany( | |
{ userId: user.userId }, | |
searchOpts, | |
pagination | |
); | |
} | |
@Get(':id') | |
async getOne( | |
@ReqUser() user: IRequestUserObject, | |
@Param('id', new ParseUUIDPipe()) id: string | |
): Promise<I<%=className%>> { | |
return this.<%=propertyName%>Service.getOne(id, user.userId); | |
} | |
@Post('') | |
async createOne( | |
@ReqUser() user: IRequestUserObject, | |
@Body() data: Create<%= className %>Dto | |
): Promise<I<%=className%>> { | |
return this.<%=propertyName%>Service.createOne({ | |
userId: user.userId, | |
...data, | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {IBaseModel} from './base.model'; | |
import {IUserModel} from './user.model'; | |
export interface I<%=className%>Relations { | |
user?: IUserModel; | |
} | |
export interface I<%= className %> extends IBaseModel { | |
userId: string; | |
} | |
export type ICreate<%= className %> = Omit<I<%=className%>, keyof IBaseModel>; | |
export type IUpdate<%= className %> = Partial<ICreate<%= className %>>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Column, Entity } from 'typeorm'; | |
import { I<%=className%> } from '@myapp/shared/domain'; | |
import { BaseOrmEntity } from './base.orm-entity'; | |
@Entity('<%= name %>') | |
export class <%= className %>OrmEntity extends BaseOrmEntity implements I<%=className%> { | |
@Column({ | |
type: String | |
}) | |
userId!: string; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { I<%=className%>Repository } from '@myapp/server/core/domain-services'; | |
import { I<%=className%> } from '@myapp/shared/domain'; | |
export class <%=className%>OrmRepositoryAdapter implements I<%=className%>Repository { | |
constructor() {} | |
async getMany(): Promise<I<%=className%>[]> { | |
throw new Error('Method not implemented.'); | |
} | |
async getOne(): Promise<I<%=className%>> { | |
throw new Error('Method not implemented.'); | |
} | |
async createOne(): Promise<I<%=className%>> { | |
throw new Error('Method not implemented.'); | |
} | |
async updateOne(): Promise<I<%=className%>> { | |
throw new Error('Method not implemented.'); | |
} | |
async deleteOne(): Promise<null> { | |
throw new Error('Method not implemented.'); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
I<%=className%>, | |
ICreate<%= className %>, | |
IUpdate<%= className %> | |
} from '@myapp/shared/domain'; | |
export abstract class I<%=className%>Repository { | |
abstract getMany(): Promise<I<%=className%>[]>; | |
abstract getOne(id: string): Promise<I<%=className%>>; | |
abstract createOne(data: ICreate<%= className %>): Promise<I<%=className%>>; | |
abstract updateOne(id: string, data: IUpdate<%=className%>): Promise<I<%=className%>>; | |
abstract deleteOne(id: string): Promise<null>; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { I<%= className %>Repository } from '@myapp/server/core/domain-services'; | |
import { I<%= className %> } from '@myapp/shared/domain'; | |
import { Injectable, Logger } from '@nestjs/common'; | |
import { BaseCrudService } from './base-crud.service'; | |
@Injectable() | |
export class <%= className %>Service extends BaseCrudService<I<%= className %>> { | |
constructor(private repo: I<%= className %>Repository) { | |
super(repo, new Logger(<%= className %>Service.name)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// import { IsOptional, IsString, MaxLength, MinLength } from 'class-validator'; | |
import { I<%= className %>, IBaseModel } from '@myapp/shared/domain'; | |
// import { ApiProperty } from '@nestjs/swagger'; | |
/** | |
* Entity doesn't need the base properties specified manually, and the userId will be inserted | |
* by the controller after reading the JWT. | |
*/ | |
export class Create<%= className %>Dto implements Omit<I<%= className %>, keyof IBaseModel | 'userId'> { | |
/** EXAMPLE */ | |
// @ApiProperty({ | |
// type: String, | |
// nullable: false, | |
// required: true, | |
// example: 'Protein', | |
// }) | |
// @IsString() | |
// @MinLength(2) | |
// @MaxLength(50) | |
// userId!: string; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as path from 'path'; | |
import { SourceFile, SyntaxKind } from 'ts-morph'; | |
import { formatFiles, generateFiles, names, Tree } from '@nx/devkit'; | |
import { TypeormEntityGeneratorSchema } from './schema'; | |
import { FileUpdates, updateSourceFiles } from './ts-morph-helper'; | |
export async function typeormEntityGenerator( | |
tree: Tree, | |
options: TypeormEntityGeneratorSchema | |
) { | |
const nameVariants = names(options.entityName); | |
generateFiles(tree, path.join(__dirname, 'files'), '', { ...nameVariants }); | |
const updates: FileUpdates = { | |
['libs/shared/domain/src/lib/models/index.ts']: ( | |
sourceFile: SourceFile | |
) => { | |
sourceFile.addExportDeclaration({ | |
moduleSpecifier: `./${nameVariants.fileName}.model`, | |
}); | |
}, | |
['libs/server/core/domain-services/src/lib/repositories/index.ts']: ( | |
sourceFile: SourceFile | |
) => { | |
sourceFile.addExportDeclaration({ | |
moduleSpecifier: `./${nameVariants.fileName}.repository`, | |
}); | |
}, | |
['libs/server/core/application-services/src/index.ts']: ( | |
sourceFile: SourceFile | |
) => { | |
sourceFile.addExportDeclaration({ | |
moduleSpecifier: `./lib/${nameVariants.fileName}.service`, | |
}); | |
}, | |
['libs/server/infrastructure/src/lib/entities/index.ts']: ( | |
sourceFile: SourceFile | |
) => { | |
sourceFile.addExportDeclaration({ | |
moduleSpecifier: `./${nameVariants.fileName}.orm-entity`, | |
}); | |
}, | |
['libs/server/infrastructure/src/lib/repositories/index.ts']: ( | |
sourceFile: SourceFile | |
) => { | |
sourceFile.addExportDeclaration({ | |
moduleSpecifier: `./${nameVariants.fileName}.orm-repository-adapter`, | |
}); | |
}, | |
['libs/server/shell/src/lib/db.module.ts']: (sourceFile: SourceFile) => { | |
sourceFile.addImportDeclaration({ | |
moduleSpecifier: `@myapp/server/infrastructure`, | |
namedImports: [`${nameVariants.className}OrmEntity`], | |
}); | |
const entityArray = sourceFile | |
.getDescendantsOfKind(SyntaxKind.ArrayLiteralExpression) | |
.find( | |
(n) => | |
n.getText().includes('OrmEntity') && | |
!n.getText().includes('Subscriber') | |
) | |
.asKind(SyntaxKind.ArrayLiteralExpression); | |
entityArray.addElement(`${nameVariants.className}OrmEntity`); | |
}, | |
['libs/server/shell/src/lib/server-shell.module.ts']: ( | |
sourceFile: SourceFile | |
) => { | |
sourceFile.addImportDeclaration({ | |
moduleSpecifier: `@myapp/server/core/application-services`, | |
namedImports: [`${nameVariants.className}Service`], | |
}); | |
sourceFile.addImportDeclaration({ | |
moduleSpecifier: `@myapp/server/core/domain-services`, | |
namedImports: [`I${nameVariants.className}Repository`], | |
}); | |
sourceFile.addImportDeclaration({ | |
moduleSpecifier: `@myapp/server/infrastructure`, | |
namedImports: [`${nameVariants.className}OrmRepositoryAdapter`], | |
}); | |
const serviceArray = sourceFile | |
.getDescendantsOfKind(SyntaxKind.ArrayLiteralExpression) | |
.find( | |
(n) => | |
n.getText().includes('Service') && | |
!n.getText().includes('RepositoryAdapter') | |
) | |
.asKind(SyntaxKind.ArrayLiteralExpression); | |
serviceArray.addElement(`${nameVariants.className}Service`); | |
const adapterArray = sourceFile | |
.getDescendantsOfKind(SyntaxKind.ArrayLiteralExpression) | |
.find((n) => n.getText().includes('RepositoryAdapter')) | |
.asKind(SyntaxKind.ArrayLiteralExpression); | |
adapterArray.addElement( | |
`{provide: I${nameVariants.className}Repository, useClass: ${nameVariants.className}OrmRepositoryAdapter}` | |
); | |
}, | |
['libs/server/ui-rest/src/lib/server-ui-rest.module.ts']: ( | |
sourceFile: SourceFile | |
) => { | |
sourceFile.addImportDeclaration({ | |
moduleSpecifier: `./controllers/${nameVariants.fileName}.controller`, | |
namedImports: [`${nameVariants.className}Controller`], | |
}); | |
const moduleClass = sourceFile.getClass((c) => | |
c.getText().includes('@Module') | |
); | |
const moduleDecorator = moduleClass.getDecorator('Module'); | |
const moduleArgs = moduleDecorator.getArguments()[0]; | |
// console.log(`moduleArgs`, moduleArgs); | |
const controllersProp = moduleArgs | |
.getDescendantsOfKind(SyntaxKind.PropertyAssignment) | |
.find((d) => d.getText().includes('controllers')); | |
// console.log(`controllerProp`, controllersProp); | |
const controllerArray = controllersProp.getFirstChildByKindOrThrow( | |
SyntaxKind.ArrayLiteralExpression | |
); | |
controllerArray.addElement(`${nameVariants.className}Controller`); | |
}, | |
}; | |
updateSourceFiles(tree, updates); | |
await formatFiles(tree); | |
} | |
export default typeormEntityGenerator; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export interface TypeormEntityGeneratorSchema { | |
entityName: string; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// copied from //https://ngserve.io/nx-how-to-write-a-generator/ | |
import { Project, SourceFile } from 'ts-morph'; | |
import { Tree } from '@nx/devkit'; | |
export type UpdateFileContent = (sourceFile: SourceFile) => void; | |
export type FileUpdates = Record<string, UpdateFileContent>; | |
export type UpdateFileDelegate = (tree: Tree, project: Project) => void; | |
function updateFile( | |
path, | |
updateFileContent: UpdateFileContent | |
): UpdateFileDelegate { | |
return (tree: Tree, project: Project): void => { | |
const fileContents = tree.read(path).toString(); | |
const sourceFile = project.createSourceFile(path, fileContents); | |
updateFileContent(sourceFile); | |
sourceFile.saveSync(); | |
}; | |
} | |
// need to know the source file being updated | |
// provide a deleagate the SourceFile to update | |
export const updateSourceFiles = (tree: Tree, fileUpdates: FileUpdates) => { | |
const project = new Project({ | |
useInMemoryFileSystem: true, | |
}); | |
const fs = project.getFileSystem(); | |
Object.keys(fileUpdates).forEach((path) => { | |
const fileUpdate = updateFile( | |
path, | |
fileUpdates[path] | |
) as UpdateFileDelegate; | |
fileUpdate(tree, project); | |
tree.write(path, fs.readFileSync(path)); | |
}); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment