@nestledjs/api 0.0.1

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 (132) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +11 -0
  3. package/eslint.config.cjs +28 -0
  4. package/generators.json +69 -0
  5. package/package.json +21 -0
  6. package/project.json +47 -0
  7. package/src/account/files/data-access/src/index.ts__tmpl__ +5 -0
  8. package/src/account/files/data-access/src/lib/api-account-data-access.module.ts__tmpl__ +10 -0
  9. package/src/account/files/data-access/src/lib/api-account-data-access.service.ts__tmpl__ +152 -0
  10. package/src/account/files/data-access/src/lib/dto/account-create-email.input.ts__tmpl__ +9 -0
  11. package/src/account/files/data-access/src/lib/dto/account-update-password.input.ts__tmpl__ +16 -0
  12. package/src/account/files/data-access/src/lib/dto/account-update-profile.input.ts__tmpl__ +25 -0
  13. package/src/account/files/feature/src/index.ts__tmpl__ +1 -0
  14. package/src/account/files/feature/src/lib/api-account-feature.module.ts__tmpl__ +9 -0
  15. package/src/account/files/feature/src/lib/api-account-feature.resolver.ts__tmpl__ +83 -0
  16. package/src/account/generator.spec.ts +71 -0
  17. package/src/account/generator.ts +20 -0
  18. package/src/account/schema.d.ts +3 -0
  19. package/src/account/schema.json +13 -0
  20. package/src/app/files/src/app.config.ts__tmpl__ +66 -0
  21. package/src/app/files/src/app.module.ts__tmpl__ +43 -0
  22. package/src/app/files/src/applogger.middleware.ts__tmpl__ +21 -0
  23. package/src/app/files/src/main.ts__tmpl__ +33 -0
  24. package/src/app/files/webpack.config.js__tmpl__ +54 -0
  25. package/src/app/generator.spec.ts +112 -0
  26. package/src/app/generator.ts +105 -0
  27. package/src/app/schema.d.ts +1 -0
  28. package/src/app/schema.json +9 -0
  29. package/src/config/files/src/index.ts__tmpl__ +3 -0
  30. package/src/config/files/src/lib/config.service.ts__tmpl__ +51 -0
  31. package/src/config/files/src/lib/configuration.ts__tmpl__ +32 -0
  32. package/src/config/files/src/lib/validation.ts__tmpl__ +21 -0
  33. package/src/config/generator.spec.ts +47 -0
  34. package/src/config/generator.ts +16 -0
  35. package/src/config/schema.d.ts +3 -0
  36. package/src/config/schema.json +13 -0
  37. package/src/core/files/data-access/src/index.ts__tmpl__ +5 -0
  38. package/src/core/files/data-access/src/lib/api-core-data-access.module.ts__tmpl__ +9 -0
  39. package/src/core/files/data-access/src/lib/api-core-data-access.service.ts__tmpl__ +97 -0
  40. package/src/core/files/data-access/src/lib/api-core-pub-sub.ts__tmpl__ +37 -0
  41. package/src/core/files/data-access/src/lib/dto/core-paging.input.ts__tmpl__ +26 -0
  42. package/src/core/files/data-access/src/lib/dto/multi-select-input.ts__tmpl__ +7 -0
  43. package/src/core/files/data-access/src/lib/models/core-paging.ts__tmpl__ +19 -0
  44. package/src/core/files/feature/src/index.ts__tmpl__ +2 -0
  45. package/src/core/files/feature/src/lib/api-core-feature.controller.ts__tmpl__ +12 -0
  46. package/src/core/files/feature/src/lib/api-core-feature.module.ts__tmpl__ +86 -0
  47. package/src/core/files/feature/src/lib/api-core-feature.resolver.ts__tmpl__ +12 -0
  48. package/src/core/files/feature/src/lib/api-core-feature.service.ts__tmpl__ +55 -0
  49. package/src/core/files/feature/src/lib/config/configuration.ts__tmpl__ +32 -0
  50. package/src/core/files/feature/src/lib/config/validation.ts__tmpl__ +25 -0
  51. package/src/core/files/feature/src/lib/plugins/complexity.plugin.ts__tmpl__ +51 -0
  52. package/src/core/files/feature/src/lib/plugins/logging.plugin.ts__tmpl__ +17 -0
  53. package/src/core/files/models/src/index.ts__tmpl__ +1 -0
  54. package/src/core/files/models/src/lib/generate-models.ts__tmpl__ +294 -0
  55. package/src/core/files/models/src/lib/models/core-paging.model.ts__tmpl__ +25 -0
  56. package/src/core/generator.spec.ts +85 -0
  57. package/src/core/generator.ts +35 -0
  58. package/src/core/schema.d.ts +3 -0
  59. package/src/core/schema.json +13 -0
  60. package/src/custom/generator.spec.ts +75 -0
  61. package/src/custom/generator.ts +239 -0
  62. package/src/custom/schema.json +21 -0
  63. package/src/custom/schema.ts +5 -0
  64. package/src/extended/generator.spec.ts +95 -0
  65. package/src/extended/generator.ts +161 -0
  66. package/src/extended/index.ts +1 -0
  67. package/src/extended/schema.json +12 -0
  68. package/src/extended/schema.ts +3 -0
  69. package/src/generate-crud/files/data-access/src/index.ts__tmpl__ +3 -0
  70. package/src/generate-crud/files/data-access/src/lib/api-crud-data-access.module.ts__tmpl__ +11 -0
  71. package/src/generate-crud/files/data-access/src/lib/api-crud-data-access.service.ts__tmpl__ +72 -0
  72. package/src/generate-crud/files/data-access/src/lib/dto/index.ts__tmpl__ +224 -0
  73. package/src/generate-crud/files/feature/.gitkeep +0 -0
  74. package/src/generate-crud/generator.spec.ts +84 -0
  75. package/src/generate-crud/generator.ts +354 -0
  76. package/src/generate-crud/schema.json +32 -0
  77. package/src/generate-crud/schema.ts +8 -0
  78. package/src/index.ts +13 -0
  79. package/src/plugin/generator.spec.ts +18 -0
  80. package/src/plugin/generator.ts +74 -0
  81. package/src/plugin/schema.json +14 -0
  82. package/src/plugin/schema.ts +4 -0
  83. package/src/prisma/files/src/index.ts__tmpl__ +1 -0
  84. package/src/prisma/files/src/lib/.gitkeep +1 -0
  85. package/src/prisma/files/src/lib/schemas/schema.prisma__tmpl__ +402 -0
  86. package/src/prisma/files/src/lib/seed/seed-data/iso-3166-countries.ts__tmpl__ +3239 -0
  87. package/src/prisma/files/src/lib/seed/seed-data/seed-users.ts__tmpl__ +32 -0
  88. package/src/prisma/files/src/lib/seed/seed.ts__tmpl__ +64 -0
  89. package/src/prisma/generator.spec.ts +60 -0
  90. package/src/prisma/generator.ts +61 -0
  91. package/src/prisma/schema.d.ts +3 -0
  92. package/src/prisma/schema.json +13 -0
  93. package/src/setup/generator.md +49 -0
  94. package/src/setup/generator.spec.ts +18 -0
  95. package/src/setup/generator.ts +106 -0
  96. package/src/setup/schema.json +8 -0
  97. package/src/smtp-mailer/files/data-access/src/index.ts__tmpl__ +2 -0
  98. package/src/smtp-mailer/files/data-access/src/lib/api-smtp-mailer-data-access.module.ts__tmpl__ +10 -0
  99. package/src/smtp-mailer/files/data-access/src/lib/api-smtp-mailer-data-access.service.ts__tmpl__ +61 -0
  100. package/src/smtp-mailer/generator.spec.ts +41 -0
  101. package/src/smtp-mailer/generator.ts +14 -0
  102. package/src/smtp-mailer/schema.d.ts +0 -0
  103. package/src/smtp-mailer/schema.json +7 -0
  104. package/src/user/files/data-access/src/index.ts__tmpl__ +5 -0
  105. package/src/user/files/data-access/src/lib/api-user-data-access.module.ts__tmpl__ +10 -0
  106. package/src/user/files/data-access/src/lib/api-user-data-access.service.ts__tmpl__ +119 -0
  107. package/src/user/files/data-access/src/lib/dto/admin-create-user.input.ts__tmpl__ +20 -0
  108. package/src/user/files/data-access/src/lib/dto/admin-update-user.input.ts__tmpl__ +29 -0
  109. package/src/user/files/feature/src/index.ts__tmpl__ +1 -0
  110. package/src/user/files/feature/src/lib/api-user-feature-admin.resolver.ts__tmpl__ +57 -0
  111. package/src/user/files/feature/src/lib/api-user-feature.module.ts__tmpl__ +10 -0
  112. package/src/user/files/feature/src/lib/api-user-feature.resolver.ts__tmpl__ +17 -0
  113. package/src/user/generator.spec.ts +41 -0
  114. package/src/user/generator.ts +15 -0
  115. package/src/user/schema.d.ts +0 -0
  116. package/src/user/schema.json +7 -0
  117. package/src/utils/files/src/index.ts__tmpl__ +3 -0
  118. package/src/utils/files/src/lib/decorators/ctx-user.decorator.ts__tmpl__ +6 -0
  119. package/src/utils/files/src/lib/guards/gql-auth-admin.guard.ts__tmpl__ +39 -0
  120. package/src/utils/files/src/lib/guards/gql-auth.guard.ts__tmpl__ +11 -0
  121. package/src/utils/generator.ts +14 -0
  122. package/src/utils/schema.json +8 -0
  123. package/src/workspace-setup/generator.md +39 -0
  124. package/src/workspace-setup/generator.spec.ts +82 -0
  125. package/src/workspace-setup/generator.ts +49 -0
  126. package/src/workspace-setup/lib/helpers.ts +142 -0
  127. package/src/workspace-setup/schema.d.ts +3 -0
  128. package/src/workspace-setup/schema.json +7 -0
  129. package/tsconfig.json +16 -0
  130. package/tsconfig.lib.json +23 -0
  131. package/tsconfig.spec.json +22 -0
  132. package/vite.config.mts +37 -0
@@ -0,0 +1,32 @@
1
+ import { UserRole } from '../../prisma-generated'
2
+
3
+
4
+ export const seedUsers = [
5
+ {
6
+ id: '1',
7
+ firstName: 'SuperAdmin',
8
+ lastName: 'User',
9
+ email: 'superadmin@example.com',
10
+ displayName: 'Super Admin',
11
+ role: UserRole.SUPER_ADMIN,
12
+ password: 'password123',
13
+ },
14
+ {
15
+ id: '2',
16
+ firstName: 'Admin',
17
+ lastName: 'User',
18
+ email: 'admin@example.com',
19
+ displayName: 'Admin User',
20
+ role: UserRole.ADMIN,
21
+ password: 'password123',
22
+ },
23
+ {
24
+ id: '3',
25
+ firstName: 'User',
26
+ lastName: 'User',
27
+ email: 'user@example.com',
28
+ displayName: 'Regular User',
29
+ role: UserRole.USER,
30
+ password: 'password123',
31
+ },
32
+ ];
@@ -0,0 +1,64 @@
1
+ import { PrismaClient } from '../prisma-generated'
2
+ import { countries } from './seed-data/iso-3166-countries'
3
+ import { seedUsers } from './seed-data/seed-users'
4
+ import { hashSync } from 'bcryptjs'
5
+
6
+ const prisma = new PrismaClient()
7
+ async function main() {
8
+ for (const country of countries) {
9
+ await prisma.country.upsert({
10
+ where: { alpha2: country['alpha-2'] },
11
+ update: {},
12
+ create: {
13
+ name: country.name,
14
+ alpha2: country['alpha-2'],
15
+ alpha3: country['alpha-3'],
16
+ countryCode: country['country-code'],
17
+ iso3166_2: country['iso_3166-2'],
18
+ region: country.region,
19
+ subRegion: country['sub-region'],
20
+ intermediateRegion: country['intermediate-region'],
21
+ regionCode: country['region-code'],
22
+ subRegionCode: country['sub-region-code'],
23
+ intermediateRegionCode: country['intermediate-region-code'],
24
+ },
25
+ })
26
+ }
27
+ for (const user of seedUsers) {
28
+ try {
29
+ await prisma.user.upsert({
30
+ where: { id: user.id },
31
+ update: {},
32
+ create: {
33
+ firstName: user.firstName,
34
+ lastName: user.lastName,
35
+ displayName: user.displayName,
36
+ emails: {
37
+ create: {
38
+ email: user.email,
39
+ primary: true,
40
+ },
41
+ },
42
+ password: hashSync(user.password, 10),
43
+ role: user.role,
44
+ },
45
+ });
46
+ console.log(`User ${user.displayName} seeded.`);
47
+ } catch (e) {
48
+ if ((e as any).code === 'P2002' && (e as any).meta?.target?.includes('displayName')) {
49
+ console.log(`User with displayName \"${user.displayName}\" already exists. Skipping.`);
50
+ } else {
51
+ throw e;
52
+ }
53
+ }
54
+ }
55
+ }
56
+ main()
57
+ .then(async () => {
58
+ await prisma.$disconnect()
59
+ })
60
+ .catch(async (e) => {
61
+ console.error(e)
62
+ await prisma.$disconnect()
63
+ process.exit(1)
64
+ })
@@ -0,0 +1,60 @@
1
+ import { formatFiles, installPackagesTask, Tree } from '@nx/devkit'
2
+ import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'
3
+ import { describe, expect, it, vi } from 'vitest'
4
+ import { apiLibraryGenerator } from '@nestledjs/utils'
5
+
6
+ import generator from './generator'
7
+
8
+ vi.mock('@nestledjs/utils', async () => {
9
+ const actual = await vi.importActual('@nestledjs/utils')
10
+ return {
11
+ ...actual,
12
+ apiLibraryGenerator: vi.fn(),
13
+ }
14
+ })
15
+
16
+ vi.mock('@nx/devkit', async () => {
17
+ const actual = await vi.importActual('@nx/devkit')
18
+ return {
19
+ ...actual,
20
+ formatFiles: vi.fn(),
21
+ installPackagesTask: vi.fn(),
22
+ }
23
+ })
24
+
25
+ describe('prisma generator', () => {
26
+ let tree: Tree
27
+
28
+ beforeEach(() => {
29
+ tree = createTreeWithEmptyWorkspace()
30
+ tree.write('package.json', JSON.stringify({ name: 'test' }))
31
+ })
32
+
33
+ it('should run successfully', async () => {
34
+ const callback = await generator(tree)
35
+ callback()
36
+
37
+ const packageJson = JSON.parse(tree.read('package.json', 'utf-8'))
38
+
39
+ expect(packageJson.prisma).toEqual({
40
+ schema: 'libs/api/prisma/src/lib/schemas',
41
+ seed: 'ts-node --project libs/api/core/models/tsconfig.lib.json libs/api/prisma/src/lib/seed/seed.ts',
42
+ })
43
+
44
+ expect(packageJson.scripts['generate:models']).toBe(
45
+ 'ts-node --project libs/api/core/models/tsconfig.lib.json libs/api/core/models/src/lib/generate-models.ts',
46
+ )
47
+
48
+ expect(apiLibraryGenerator).toHaveBeenCalledWith(tree, { name: 'prisma', overwrite: false }, expect.any(String))
49
+ expect(formatFiles).toHaveBeenCalledWith(tree)
50
+ expect(installPackagesTask).toHaveBeenCalledWith(tree)
51
+ })
52
+
53
+ it('should pass overwrite flag to apiLibraryGenerator when overwrite is true', async () => {
54
+ tree.write('package.json', JSON.stringify({ name: 'test' }))
55
+ const callback = await generator(tree, { overwrite: true })
56
+ callback()
57
+
58
+ expect(apiLibraryGenerator).toHaveBeenCalledWith(tree, { name: 'prisma', overwrite: true }, expect.any(String))
59
+ })
60
+ })
@@ -0,0 +1,61 @@
1
+ import { formatFiles, installPackagesTask, joinPathFragments, Tree, updateJson } from '@nx/devkit'
2
+ import { apiLibraryGenerator } from '@nestledjs/utils'
3
+ import { ApiPrismaGeneratorSchema } from './schema'
4
+
5
+ export default async function generateLibraries(tree: Tree, options: ApiPrismaGeneratorSchema = {}) {
6
+ const templateRootPath = joinPathFragments(__dirname, './files')
7
+ const overwrite = options.overwrite === true
8
+
9
+ // Update package.json
10
+ updateJson(tree, 'package.json', (json) => {
11
+ // Add prisma schema path
12
+ json.prisma = {
13
+ schema: 'libs/api/prisma/src/lib/schemas',
14
+ seed: 'ts-node --project libs/api/core/models/tsconfig.lib.json libs/api/prisma/src/lib/seed/seed.ts',
15
+ }
16
+ // Add GraphQL model generation script for the 'core' library
17
+ if (!json.scripts) {
18
+ json.scripts = {}
19
+ }
20
+ if (!json.scripts['generate:models']) {
21
+ json.scripts['generate:models'] =
22
+ 'ts-node --project libs/api/core/models/tsconfig.lib.json libs/api/core/models/src/lib/generate-models.ts'
23
+ }
24
+
25
+ // Add all requested prisma scripts if not already present
26
+ if (!json.scripts['prisma:apply']) {
27
+ json.scripts['prisma:apply'] = 'pnpm prisma:format && pnpm prisma db push'
28
+ }
29
+ if (!json.scripts['prisma:db-push']) {
30
+ json.scripts['prisma:db-push'] = 'pnpm prisma db push'
31
+ }
32
+ if (!json.scripts['prisma:format']) {
33
+ json.scripts['prisma:format'] = 'pnpm prisma format'
34
+ }
35
+ if (!json.scripts['prisma:generate']) {
36
+ json.scripts['prisma:generate'] = 'pnpm prisma generate'
37
+ }
38
+ if (!json.scripts['prisma:migrate']) {
39
+ json.scripts['prisma:migrate'] = 'pnpm prisma migrate save && pnpm prisma migrate up'
40
+ }
41
+ if (!json.scripts['prisma:seed']) {
42
+ json.scripts['prisma:seed'] =
43
+ 'ts-node --project libs/api/prisma/tsconfig.lib.json libs/api/prisma/src/lib/seed/seed.ts'
44
+ }
45
+ if (!json.scripts['prisma:studio']) {
46
+ json.scripts['prisma:studio'] = 'pnpm nx prisma:studio api'
47
+ }
48
+ if (!json.scripts['prisma:reset']) {
49
+ json.scripts['prisma:reset'] = 'pnpm prisma migrate reset && pnpm prisma:seed'
50
+ }
51
+ return json
52
+ })
53
+
54
+ await apiLibraryGenerator(tree, { name: 'prisma', overwrite }, templateRootPath)
55
+
56
+ await formatFiles(tree)
57
+
58
+ return () => {
59
+ installPackagesTask(tree)
60
+ }
61
+ }
@@ -0,0 +1,3 @@
1
+ export interface ApiPrismaGeneratorSchema {
2
+ overwrite?: boolean
3
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "https://json-schema.org/schema",
3
+ "$id": "ApiPrisma",
4
+ "title": "Create the API Prisma library",
5
+ "type": "object",
6
+ "properties": {
7
+ "overwrite": {
8
+ "type": "boolean",
9
+ "description": "Whether to overwrite the existing prisma library if it exists.",
10
+ "default": false
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,49 @@
1
+ # Overview
2
+ This code is a generator function that sets up a NestJS-based API project with various dependencies and configurations. The generator performs the following tasks:
3
+
4
+ 1. Adds necessary dependencies to the `package.json` file.
5
+ 2. Updates the `tsconfig.base.json` file to set the `baseUrl` for path aliases and remove the `emitDeclarationOnly` option if it exists.
6
+ 3. Updates the `package.json` file to:
7
+ - Initialize the `pnpm.onlyBuiltDependencies` array if it doesn't exist.
8
+ - Add required packages to the `pnpm.onlyBuiltDependencies` array.
9
+ - Add a `generate:models` script to generate GraphQL models.
10
+ 4. Removes the `workspaces` section from the `package.json` file if it exists.
11
+ 5. Runs `pnpm install` after the generator completes.
12
+
13
+ # Functions/Classes
14
+ ## `updateTypeScriptConfig(tree: Tree)`
15
+ - This function updates the `tsconfig.base.json` file in the project.
16
+ - It sets the `baseUrl` for path aliases and removes the `emitDeclarationOnly` option if it exists.
17
+ - The updated configuration is then written back to the file.
18
+
19
+ ## `updatePackageJson(tree: Tree)`
20
+ - This function updates the `package.json` file in the project.
21
+ - It initializes the `pnpm` section and the `pnpm.onlyBuiltDependencies` array if they don't exist.
22
+ - It adds a list of required packages to the `pnpm.onlyBuiltDependencies` array if they don't already exist.
23
+ - It adds a `generate:models` script to the `scripts` section.
24
+ - The updated `package.json` file is then written back to the file.
25
+
26
+ ## `removeWorkspacesFromPackageJson(tree: Tree)`
27
+ - This function removes the `workspaces` section from the `package.json` file if it exists.
28
+
29
+ ## `apiSetupGenerator(tree: Tree): Promise<GeneratorCallback>`
30
+ - This is the main generator function that orchestrates the entire setup process.
31
+ - It calls the `addDependenciesToPackageJson` function to add the necessary dependencies to the `package.json` file.
32
+ - It calls the `updateTypeScriptConfig`, `updatePackageJson`, and `removeWorkspacesFromPackageJson` functions to update the respective configuration files.
33
+ - Finally, it returns a callback function that will run `pnpm install` after the generator completes.
34
+
35
+ # Behavior Flow
36
+ 1. The `apiSetupGenerator` function is called with a `Tree` object, which represents the file system of the project.
37
+ 2. The function adds the necessary dependencies to the `package.json` file using the `addDependenciesToPackageJson` function.
38
+ 3. The `updateTypeScriptConfig` function is called to update the `tsconfig.base.json` file.
39
+ 4. The `updatePackageJson` function is called to update the `package.json` file with the `pnpm` settings and the `generate:models` script.
40
+ 5. The `removeWorkspacesFromPackageJson` function is called to remove the `workspaces` section from the `package.json` file if it exists.
41
+ 6. The generator function returns a callback that will run `pnpm install` after the generator completes.
42
+
43
+ # Key Points
44
+ - This generator is designed to set up a NestJS-based API project with various dependencies and configurations.
45
+ - It ensures that the necessary TypeScript and package.json configurations are in place for the project to function correctly.
46
+ - The generator relies on the `@nx/devkit` library to perform file system operations and update configuration files.
47
+ - The `pnpm.onlyBuiltDependencies` array is used to specify the packages that should be built from source, which can improve build performance.
48
+ - The `generate:models` script is added to the `package.json` file to provide a convenient way to generate GraphQL models.
49
+ - The removal of the `workspaces` section from the `package.json` file is necessary to ensure compatibility with the `pnpm` package manager.
@@ -0,0 +1,18 @@
1
+ import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'
2
+ import { Tree } from '@nx/devkit'
3
+ import { apiSetupGenerator } from './generator'
4
+
5
+ describe('api-dependencies generator', () => {
6
+ let tree: Tree
7
+
8
+ beforeEach(() => {
9
+ tree = createTreeWithEmptyWorkspace()
10
+ })
11
+
12
+ it('should add required dependencies to package.json', async () => {
13
+ await apiSetupGenerator(tree)
14
+ // Note: We can't easily test package.json modifications in the tree
15
+ // since addDependenciesToPackageJson is mocked in tests
16
+ expect(tree).toBeDefined()
17
+ })
18
+ })
@@ -0,0 +1,106 @@
1
+ import { addDependenciesToPackageJson, GeneratorCallback, Tree } from '@nx/devkit'
2
+ import { pnpmInstallCallback, updatePnpmWorkspaceConfig } from '@nestledjs/utils'
3
+
4
+ function ensureNpmrc(tree: Tree) {
5
+ const npmrcPath = '.npmrc'
6
+ const content = 'ignore-workspace-root-check=true\n'
7
+ tree.write(npmrcPath, content)
8
+ }
9
+
10
+ export async function apiSetupGenerator(tree: Tree): Promise<GeneratorCallback> {
11
+ // Add dependencies
12
+ addDependenciesToPackageJson(
13
+ tree,
14
+ {
15
+ '@nestjs/apollo': '^13.1.0',
16
+ '@nestjs/axios': '^4.0.0',
17
+ '@nestjs/common': '^11.1.3',
18
+ '@nestjs/config': '^4.0.2',
19
+ '@nestjs/core': '^11.1.3',
20
+ '@nestjs/graphql': '^13.1.0',
21
+ '@nestjs/jwt': '^11.0.0',
22
+ '@nestjs/passport': '^11.0.5',
23
+ '@nestjs/platform-express': '^11.1.3',
24
+ '@paljs/plugins': '^8.2.0',
25
+ axios: '^1.9.0',
26
+ bcryptjs: '^3.0.2',
27
+ express: '^5.1.0',
28
+ graphql: '^16.11.0',
29
+ 'graphql-query-complexity': '1.1.0',
30
+ 'graphql-scalars': '^1.24.2',
31
+ 'graphql-subscriptions': '^3.0.0',
32
+ 'graphql-ws': '^6.0.5',
33
+ joi: '^17.13.3',
34
+ nodemailer: '^7.0.3',
35
+ 'reflect-metadata': '^0.2.2',
36
+ rxjs: '^7.8.2',
37
+ 'type-graphql': '^2.0.0-rc.2',
38
+ 'graphql-fields': '^2.0.3',
39
+ 'class-validator': '^0.14.0',
40
+ 'class-transformer': '^0.5.1',
41
+ 'graphql-redis-subscriptions': '^2.7.0',
42
+ ioredis: '^5.6.1',
43
+ 'cookie-parser': '^1.4.7',
44
+ 'passport-jwt': '^4.0.1',
45
+ },
46
+ {
47
+ nx: '21.2.0',
48
+ '@nx/js': '21.2.0',
49
+ '@nx/nest': '21.2.0',
50
+ '@nx/node': '21.2.0',
51
+ '@nx/webpack': '21.2.0',
52
+ '@nestjs/schematics': '^11.0.5',
53
+ '@nestjs/testing': '^11.1.3',
54
+ '@prisma/extension-optimize': '2.0.0',
55
+ '@swc-node/register': '~1.10.10',
56
+ '@swc/cli': '~0.7.7',
57
+ '@swc/core': '~1.12.1',
58
+ '@types/cookie-parser': '^1.4.3',
59
+ '@types/express': '^5.0.3',
60
+ '@types/nodemailer': '^6.4.7',
61
+ '@types/passport-jwt': '^4.0.1',
62
+ '@types/node': '^24.0.1',
63
+ '@prisma/internals': '^6.9.0',
64
+ autoprefixer: '10.4.21',
65
+ jest: '^30.0.0',
66
+ 'jest-environment-node': '^30.0.0',
67
+ pg: '8.16.0',
68
+ prisma: '^6.9.0',
69
+ 'ts-node': '10.9.2',
70
+ yaml: '^2.4.2',
71
+ 'ts-loader': '^9.5.2',
72
+ 'webpack-cli': '^6.0.1',
73
+ 'webpack-node-externals': '^3.0.0',
74
+ 'tsconfig-paths-webpack-plugin': '^4.2.0',
75
+ 'prisma-graphql-type-decimal': '^3.0.1',
76
+ 'graphql-type-json': '^0.3.2',
77
+ },
78
+ )
79
+
80
+ // Update pnpm-workspace.yaml with build dependencies
81
+ const packagesToBuild = [
82
+ '@apollo/protobufjs',
83
+ '@parcel/watcher',
84
+ '@prisma/client',
85
+ '@prisma/extension-optimize',
86
+ '@prisma/engines',
87
+ 'esbuild',
88
+ 'nx',
89
+ 'prisma',
90
+ 'ioredis',
91
+ 'prisma-graphql-type-decimal',
92
+ '@nestjs/core',
93
+ 'type-graphql',
94
+ 'express',
95
+ 'unrs-resolver',
96
+ ]
97
+ updatePnpmWorkspaceConfig(tree, { onlyBuiltDependencies: packagesToBuild })
98
+
99
+ // Ensure .npmrc exists with the required content
100
+ ensureNpmrc(tree)
101
+
102
+ // Return a callback that will run after the generator completes
103
+ return pnpmInstallCallback()
104
+ }
105
+
106
+ export default apiSetupGenerator
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://json-schema.org/schema",
3
+ "cli": "nx",
4
+ "id": "api-setup",
5
+ "type": "object",
6
+ "properties": {},
7
+ "required": []
8
+ }
@@ -0,0 +1,2 @@
1
+ export * from './lib/api-smtp-mailer-data-access.module'
2
+ export * from './lib/api-smtp-mailer-data-access.service'
@@ -0,0 +1,10 @@
1
+ import { Module } from '@nestjs/common'
2
+ import { SmtpMailerService } from './api-smtp-mailer-data-access.service'
3
+ import { ApiCoreFeatureModule } from '@<%= npmScope %>/api/core/feature'
4
+
5
+ @Module({
6
+ imports: [ApiCoreFeatureModule],
7
+ providers: [SmtpMailerService],
8
+ exports: [SmtpMailerService],
9
+ })
10
+ export class SmtpMailerModule {}
@@ -0,0 +1,61 @@
1
+ import { Injectable, Logger } from '@nestjs/common'
2
+ import { createTransport, Transporter } from 'nodemailer'
3
+ import { ApiCoreFeatureService } from '@<%= npmScope %>/api/core/feature'
4
+ import { SentMessageInfo } from 'nodemailer/lib/smtp-transport'
5
+
6
+ export interface SendMailParams {
7
+ to: string
8
+ subject: string
9
+ html: string
10
+ text: string
11
+ from?: string
12
+ appSupportEmail?: string
13
+ }
14
+
15
+ @Injectable()
16
+ export class SmtpMailerService {
17
+ private readonly logger = new Logger('ApiMailerService')
18
+ private readonly mailer: Transporter<SentMessageInfo>
19
+ private readonly appEmail: string
20
+ private readonly appName: string
21
+ private readonly appSupportEmail: string
22
+
23
+ constructor(private readonly config: ApiCoreFeatureService) {
24
+ this.mailer = createTransport({
25
+ host: this.config.mailerConfig.host,
26
+ port: parseInt(this.config.mailerConfig.port),
27
+ auth: {
28
+ user: this.config.mailerConfig.auth.user,
29
+ pass: this.config.mailerConfig.auth.pass,
30
+ },
31
+ })
32
+ this.appEmail = this.config.appEmail
33
+ this.appName = this.config.appName
34
+ this.appSupportEmail = this.config.appSupportEmail
35
+ }
36
+
37
+ async send(params: SendMailParams) {
38
+ const {
39
+ to,
40
+ subject,
41
+ html,
42
+ text,
43
+ from,
44
+ appSupportEmail,
45
+ } = params
46
+
47
+ const sender = from || `${this.appName} <${this.appEmail}>`
48
+ const supportEmail = appSupportEmail || this.appSupportEmail
49
+
50
+ const res = await this.mailer.sendMail({
51
+ to,
52
+ from: sender,
53
+ subject,
54
+ html,
55
+ text,
56
+ replyTo: supportEmail,
57
+ })
58
+ this.logger.verbose(`sendMail to ${to} result: ${res.response} ${res.messageId}`)
59
+ return res
60
+ }
61
+ }
@@ -0,0 +1,41 @@
1
+ import { formatFiles, Tree } from '@nx/devkit'
2
+ import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'
3
+ import { describe, expect, it, vi } from 'vitest'
4
+ import { apiLibraryGenerator } from '@nestledjs/utils'
5
+ import { installPackagesTask } from '@nx/devkit'
6
+
7
+ import generator from './generator'
8
+
9
+ vi.mock('@nestledjs/utils', async () => {
10
+ const actual = await vi.importActual('@nestledjs/utils')
11
+ return {
12
+ ...actual,
13
+ apiLibraryGenerator: vi.fn(),
14
+ }
15
+ })
16
+
17
+ vi.mock('@nx/devkit', async () => {
18
+ const actual = await vi.importActual('@nx/devkit')
19
+ return {
20
+ ...actual,
21
+ formatFiles: vi.fn(),
22
+ installPackagesTask: vi.fn(),
23
+ }
24
+ })
25
+
26
+ describe('mailer generator', () => {
27
+ let tree: Tree
28
+
29
+ beforeEach(() => {
30
+ tree = createTreeWithEmptyWorkspace()
31
+ })
32
+
33
+ it('should run successfully', async () => {
34
+ const callback = await generator(tree)
35
+ callback()
36
+
37
+ expect(apiLibraryGenerator).toHaveBeenCalledWith(tree, { name: 'smtp-mailer' }, expect.any(String), 'data-access')
38
+ expect(formatFiles).toHaveBeenCalledWith(tree)
39
+ expect(installPackagesTask).toHaveBeenCalledWith(tree)
40
+ })
41
+ })
@@ -0,0 +1,14 @@
1
+ import { formatFiles, GeneratorCallback, installPackagesTask, joinPathFragments, Tree } from '@nx/devkit'
2
+ import { apiLibraryGenerator } from '@nestledjs/utils'
3
+
4
+ export default async function generateLibraries(tree: Tree): Promise<GeneratorCallback> {
5
+ const templateRootPath = joinPathFragments(__dirname, './files')
6
+
7
+ await apiLibraryGenerator(tree, { name: 'smtp-mailer' }, templateRootPath, 'data-access')
8
+
9
+ await formatFiles(tree)
10
+
11
+ return () => {
12
+ installPackagesTask(tree)
13
+ }
14
+ }
File without changes
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "https://json-schema.org/schema",
3
+ "$id": "ApiSMTPMailer",
4
+ "title": "Create an API Mailer Library",
5
+ "type": "object",
6
+ "properties": {}
7
+ }
@@ -0,0 +1,5 @@
1
+ export * from './lib/api-user-data-access.module'
2
+ export * from './lib/api-user-data-access.module'
3
+ export * from './lib/api-user-data-access.service'
4
+ export * from './lib/dto/admin-create-user.input'
5
+ export * from './lib/dto/admin-update-user.input'
@@ -0,0 +1,10 @@
1
+ import { Module } from '@nestjs/common'
2
+ import { ApiCoreDataAccessModule } from '@<%= npmScope %>/api/core/data-access'
3
+ import { ApiUserDataAccessService } from './api-user-data-access.service'
4
+
5
+ @Module({
6
+ imports: [ApiCoreDataAccessModule],
7
+ providers: [ApiUserDataAccessService],
8
+ exports: [ApiUserDataAccessService],
9
+ })
10
+ export class ApiUserDataAccessModule {}