@exanderal/stackcraft 0.1.1 → 0.2.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 (91) hide show
  1. package/README.md +79 -8
  2. package/dist/create/index.d.ts.map +1 -1
  3. package/dist/create/index.js +24 -1
  4. package/dist/create/index.js.map +1 -1
  5. package/dist/create/scaffold.d.ts.map +1 -1
  6. package/dist/create/scaffold.js +8 -2
  7. package/dist/create/scaffold.js.map +1 -1
  8. package/dist/create/scaffolders/__tests__/web-vite.test.js +1 -0
  9. package/dist/create/scaffolders/__tests__/web-vite.test.js.map +1 -1
  10. package/dist/create/scaffolders/api-nestjs-graphql.d.ts +3 -0
  11. package/dist/create/scaffolders/api-nestjs-graphql.d.ts.map +1 -0
  12. package/dist/create/scaffolders/api-nestjs-graphql.js +31 -0
  13. package/dist/create/scaffolders/api-nestjs-graphql.js.map +1 -0
  14. package/dist/create/scaffolders/api-nestjs-rest.d.ts.map +1 -1
  15. package/dist/create/scaffolders/api-nestjs-rest.js +19 -2
  16. package/dist/create/scaffolders/api-nestjs-rest.js.map +1 -1
  17. package/dist/create/types.d.ts +3 -1
  18. package/dist/create/types.d.ts.map +1 -1
  19. package/package.json +1 -1
  20. package/templates/api-nestjs-graphql/.prettierrc +4 -0
  21. package/templates/api-nestjs-graphql/eslint.config.mjs +34 -0
  22. package/templates/api-nestjs-graphql/nest-cli.json +8 -0
  23. package/templates/api-nestjs-graphql/package.json +81 -0
  24. package/templates/api-nestjs-graphql/project.json +21 -0
  25. package/templates/api-nestjs-graphql/src/app.module.ts +21 -0
  26. package/templates/api-nestjs-graphql/src/common/entities/base.entity.ts +21 -0
  27. package/templates/api-nestjs-graphql/src/common/repositories/entity.repository.ts +21 -0
  28. package/templates/api-nestjs-graphql/src/common/repositories/readonly-entity.repository.ts +22 -0
  29. package/templates/api-nestjs-graphql/src/common/services/entity.service.ts +24 -0
  30. package/templates/api-nestjs-graphql/src/common/services/readonly-entity.service.ts +23 -0
  31. package/templates/api-nestjs-graphql/src/main.ts +8 -0
  32. package/templates/api-nestjs-graphql/src/modules/database/database.module.ts +18 -0
  33. package/templates/api-nestjs-graphql/src/modules/health/__tests__/health.controller.spec.ts +20 -0
  34. package/templates/api-nestjs-graphql/src/modules/health/__tests__/health.service.spec.ts +18 -0
  35. package/templates/api-nestjs-graphql/src/modules/health/health.controller.ts +12 -0
  36. package/templates/api-nestjs-graphql/src/modules/health/health.module.ts +9 -0
  37. package/templates/api-nestjs-graphql/src/modules/health/health.service.ts +8 -0
  38. package/templates/api-nestjs-graphql/test/app.e2e-spec.ts +29 -0
  39. package/templates/api-nestjs-graphql/test/jest-e2e.json +9 -0
  40. package/templates/api-nestjs-graphql/tsconfig.build.json +4 -0
  41. package/templates/api-nestjs-graphql/tsconfig.json +21 -0
  42. package/templates/api-nestjs-rest/package.json +5 -2
  43. package/templates/api-nestjs-rest/project.json +1 -1
  44. package/templates/api-nestjs-rest/src/app.module.ts +8 -5
  45. package/templates/api-nestjs-rest/src/common/entities/base.entity.ts +16 -0
  46. package/templates/api-nestjs-rest/src/common/repositories/entity.repository.ts +21 -0
  47. package/templates/api-nestjs-rest/src/common/repositories/readonly-entity.repository.ts +22 -0
  48. package/templates/api-nestjs-rest/src/common/services/entity.service.ts +24 -0
  49. package/templates/api-nestjs-rest/src/common/services/readonly-entity.service.ts +23 -0
  50. package/templates/api-nestjs-rest/src/modules/database/database.module.ts +18 -0
  51. package/templates/api-nestjs-rest/src/modules/health/__tests__/health.controller.spec.ts +20 -0
  52. package/templates/api-nestjs-rest/src/modules/health/__tests__/health.service.spec.ts +18 -0
  53. package/templates/api-nestjs-rest/src/modules/health/health.controller.ts +12 -0
  54. package/templates/api-nestjs-rest/src/modules/health/health.module.ts +9 -0
  55. package/templates/api-nestjs-rest/src/modules/health/health.service.ts +8 -0
  56. package/templates/api-nestjs-rest/test/app.e2e-spec.ts +9 -5
  57. package/templates/base/package.json +5 -1
  58. package/templates/base/pnpm-workspace.yaml +1 -0
  59. package/templates/base/tools/generators/controller/files/__fileName__.controller.ts__tmpl__ +36 -0
  60. package/templates/base/tools/generators/controller/index.js +22 -0
  61. package/templates/base/tools/generators/controller/schema.json +18 -0
  62. package/templates/base/tools/generators/generators.json +19 -0
  63. package/templates/base/tools/generators/module/files/__fileName__.model.ts__tmpl__ +8 -0
  64. package/templates/base/tools/generators/module/files/__fileName__.module.ts__tmpl__ +12 -0
  65. package/templates/base/tools/generators/module/files/__fileName__.repository.ts__tmpl__ +15 -0
  66. package/templates/base/tools/generators/module/files/__fileName__.service.ts__tmpl__ +11 -0
  67. package/templates/base/tools/generators/module/files/__tests__/__fileName__.integration.spec.ts__tmpl__ +31 -0
  68. package/templates/base/tools/generators/module/graphql-files/__fileName__.model.ts__tmpl__ +11 -0
  69. package/templates/base/tools/generators/module/index.js +26 -0
  70. package/templates/base/tools/generators/module/schema.json +23 -0
  71. package/templates/base/tools/generators/package.json +6 -0
  72. package/templates/base/tools/generators/resolver/files/__fileName__.resolver.ts__tmpl__ +28 -0
  73. package/templates/base/tools/generators/resolver/index.js +22 -0
  74. package/templates/base/tools/generators/resolver/schema.json +18 -0
  75. package/templates/web-nextjs/package.json +3 -0
  76. package/templates/web-nextjs/postcss.config.mjs +7 -0
  77. package/templates/web-nextjs/src/app/globals.css +1 -0
  78. package/templates/web-nextjs/src/components/TemplateComponent/TemplateComponent.behaviour.ts +39 -0
  79. package/templates/web-nextjs/src/components/TemplateComponent/TemplateComponent.tsx +14 -0
  80. package/templates/web-vite/package.json +2 -0
  81. package/templates/web-vite/src/components/TemplateComponent/TemplateComponent.behaviour.ts +39 -0
  82. package/templates/web-vite/src/components/TemplateComponent/TemplateComponent.tsx +14 -0
  83. package/templates/web-vite/src/index.css +1 -8
  84. package/templates/web-vite/vite.config.ts +3 -3
  85. package/templates/api-nestjs-rest/src/app.controller.spec.ts +0 -22
  86. package/templates/api-nestjs-rest/src/app.controller.ts +0 -12
  87. package/templates/api-nestjs-rest/src/app.service.ts +0 -8
  88. package/templates/web-nextjs/app/globals.css +0 -10
  89. /package/templates/web-nextjs/{app → src/app}/favicon.ico +0 -0
  90. /package/templates/web-nextjs/{app → src/app}/layout.tsx +0 -0
  91. /package/templates/web-nextjs/{app → src/app}/page.tsx +0 -0
package/README.md CHANGED
@@ -10,25 +10,96 @@ Spin up a production-ready monorepo in one command.
10
10
  npx @exanderal/stackcraft
11
11
  ```
12
12
 
13
- Follow the prompts. You'll end up with an Nx monorepo with a NestJS API and your choice of Vite + React or Next.js frontend — deps installed, ready to run.
13
+ Follow the prompts — you'll have an Nx monorepo with deps installed and ready to run.
14
14
 
15
15
  ## What you get
16
16
 
17
- - Nx monorepo with `apps/` and `packages/`
18
- - NestJS REST API (`apps/api`)
19
- - Vite + React or Next.js (`apps/web`)
20
- - Every app has `dev`, `build`, `lint` scripts wired into Nx
17
+ ```
18
+ your-project/
19
+ ├── apps/
20
+ │ ├── backend/ # NestJS REST or GraphQL API
21
+ │ └── web/ # Vite + React or Next.js
22
+ ├── packages/ # shared code
23
+ └── tools/
24
+ └── generators/ # local Nx code generators
25
+ ```
26
+
27
+ ### Backend (`apps/backend`)
28
+
29
+ Choose between REST or GraphQL at setup time — both use the same underlying structure:
30
+
31
+ ```
32
+ src/
33
+ ├── modules/ # domain layer — model, repository, service, module
34
+ ├── api/ # REST controllers (REST only)
35
+ ├── resolvers/ # GraphQL resolvers (GraphQL only)
36
+ └── common/ # shared base classes
37
+ ```
38
+
39
+ - NestJS with TypeORM
40
+ - PostgreSQL or MySQL
41
+ - `EntityRepository` and `EntityService` base classes — extend them for each module
42
+ - UUID primary keys
43
+
44
+ ### Frontend (`apps/web`)
45
+
46
+ - Vite + React or Next.js
47
+ - Tailwind CSS v4
48
+ - TypeScript
49
+
50
+ ## Running the project
51
+
52
+ ```sh
53
+ pnpm dev # start all apps in parallel
54
+ pnpm build # build all apps
55
+ pnpm test # run all tests
56
+ pnpm lint # lint all apps
57
+ ```
58
+
59
+ Target a specific app:
60
+
61
+ ```sh
62
+ pnpm nx run backend:dev
63
+ pnpm nx run web:dev
64
+ ```
65
+
66
+ ## Code generation
67
+
68
+ Generate a new domain module:
69
+
70
+ ```sh
71
+ pnpm generate:module --name=trainer
72
+ # add --graphql to include @ObjectType() on the model
73
+ pnpm generate:module --name=trainer --graphql
74
+ ```
75
+
76
+ Generate a REST controller for an existing module:
77
+
78
+ ```sh
79
+ pnpm generate:controller --name=trainer
80
+ # creates apps/backend/src/api/trainer/trainer.controller.ts
81
+ ```
82
+
83
+ Generate a GraphQL resolver for an existing module:
84
+
85
+ ```sh
86
+ pnpm generate:resolver --name=trainer
87
+ # creates apps/backend/src/resolvers/trainer/trainer.resolver.ts
88
+ ```
21
89
 
22
90
  ## Stack
23
91
 
24
92
  - **Monorepo** — Nx
25
93
  - **Package manager** — pnpm or npm
26
- - **Backend** — NestJS
27
- - **Frontend** — Vite + React or Next.js
94
+ - **Backend** — NestJS, TypeORM, PostgreSQL/MySQL
95
+ - **Frontend** — Vite + React or Next.js, Tailwind CSS v4
28
96
 
29
97
  ## Roadmap
30
98
 
31
- - [ ] NestJS GraphQL + codegen
99
+ - [x] NestJS REST API
100
+ - [x] NestJS GraphQL API
101
+ - [x] Module, controller, and resolver generators
102
+ - [ ] GraphQL codegen pipeline
32
103
  - [ ] Expo mobile
33
104
  - [ ] `stackcraft add` addon system (auth, Supabase, etc.)
34
105
  - [ ] Presets and `--config` for non-interactive use
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/create/index.ts"],"names":[],"mappings":"AAKA,wBAAsB,MAAM,kBAmD3B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/create/index.ts"],"names":[],"mappings":"AAKA,wBAAsB,MAAM,kBA4E3B"}
@@ -12,6 +12,17 @@ export async function create() {
12
12
  cancel('Cancelled.');
13
13
  process.exit(0);
14
14
  }
15
+ const backend = await select({
16
+ message: 'Backend',
17
+ options: [
18
+ { value: 'nestjs-rest', label: 'NestJS REST', hint: 'REST API with TypeORM' },
19
+ { value: 'nestjs-graphql', label: 'NestJS GraphQL', hint: 'Code-first GraphQL with TypeORM' },
20
+ ],
21
+ });
22
+ if (isCancel(backend)) {
23
+ cancel('Cancelled.');
24
+ process.exit(0);
25
+ }
15
26
  const frontend = await select({
16
27
  message: 'Frontend',
17
28
  options: [
@@ -23,6 +34,17 @@ export async function create() {
23
34
  cancel('Cancelled.');
24
35
  process.exit(0);
25
36
  }
37
+ const database = await select({
38
+ message: 'Database',
39
+ options: [
40
+ { value: 'postgres', label: 'PostgreSQL', hint: 'recommended' },
41
+ { value: 'mysql', label: 'MySQL' },
42
+ ],
43
+ });
44
+ if (isCancel(database)) {
45
+ cancel('Cancelled.');
46
+ process.exit(0);
47
+ }
26
48
  const packageManager = await select({
27
49
  message: 'Package manager',
28
50
  options: [
@@ -37,7 +59,8 @@ export async function create() {
37
59
  const config = {
38
60
  projectName: projectName,
39
61
  frontend: frontend,
40
- backend: 'nestjs-rest',
62
+ backend: backend,
63
+ database: database,
41
64
  packageManager: packageManager,
42
65
  targetDir: resolve(process.cwd(), projectName),
43
66
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/create/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAA;AACtF,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAGxC,MAAM,CAAC,KAAK,UAAU,MAAM;IAC1B,KAAK,CAAC,kDAAkD,CAAC,CAAA;IAEzD,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;QAC7B,OAAO,EAAE,cAAc;QACvB,WAAW,EAAE,QAAQ;QACrB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;KAC/C,CAAC,CAAA;IACF,IAAI,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,YAAY,CAAC,CAAA;QACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC;QAC5B,OAAO,EAAE,UAAU;QACnB,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,iCAAiC,EAAE;YACjF,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,+BAA+B,EAAE;SAC7E;KACF,CAAC,CAAA;IACF,IAAI,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvB,MAAM,CAAC,YAAY,CAAC,CAAA;QACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,MAAM,CAAC;QAClC,OAAO,EAAE,iBAAiB;QAC1B,OAAO,EAAE;YACP,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE;YACrD,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE;SAC/B;KACF,CAAC,CAAA;IACF,IAAI,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,YAAY,CAAC,CAAA;QACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,MAAM,GAAkB;QAC5B,WAAW,EAAE,WAAqB;QAClC,QAAQ,EAAE,QAAoB;QAC9B,OAAO,EAAE,aAAwB;QACjC,cAAc,EAAE,cAAgC;QAChD,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAqB,CAAC;KACzD,CAAA;IAED,MAAM,CAAC,GAAG,OAAO,EAAE,CAAA;IACnB,CAAC,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAA;IACtC,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;IAC/C,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAEf,KAAK,CAAC,MAAM,MAAM,CAAC,WAAW,OAAO,MAAM,CAAC,cAAc,MAAM,CAAC,CAAA;AACnE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/create/index.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/create/scaffold.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,wBAAsB,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,iBAiBlF"}
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/create/scaffold.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C,wBAAsB,QAAQ,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,iBAqBlF"}
@@ -1,4 +1,5 @@
1
1
  import { execa } from 'execa';
2
+ import { scaffoldNestjsGraphql } from './scaffolders/api-nestjs-graphql.js';
2
3
  import { scaffoldNestjsRest } from './scaffolders/api-nestjs-rest.js';
3
4
  import { scaffoldBase } from './scaffolders/base.js';
4
5
  import { scaffoldNextjs } from './scaffolders/web-nextjs.js';
@@ -6,8 +7,13 @@ import { scaffoldVite } from './scaffolders/web-vite.js';
6
7
  export async function scaffold(config, onStep) {
7
8
  onStep('Creating workspace...');
8
9
  await scaffoldBase(config);
9
- onStep('Adding API...');
10
- await scaffoldNestjsRest(config);
10
+ onStep('Adding backend...');
11
+ if (config.backend === 'nestjs-graphql') {
12
+ await scaffoldNestjsGraphql(config);
13
+ }
14
+ else {
15
+ await scaffoldNestjsRest(config);
16
+ }
11
17
  if (config.frontend === 'vite') {
12
18
  onStep('Adding Vite + React app...');
13
19
  await scaffoldVite(config);
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/create/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAA;AAC7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAGxD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,MAAqB,EAAE,MAA6B;IACjF,MAAM,CAAC,uBAAuB,CAAC,CAAA;IAC/B,MAAM,YAAY,CAAC,MAAM,CAAC,CAAA;IAE1B,MAAM,CAAC,eAAe,CAAC,CAAA;IACvB,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAA;IAEhC,IAAI,MAAM,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;QAC/B,MAAM,CAAC,4BAA4B,CAAC,CAAA;QACpC,MAAM,YAAY,CAAC,MAAM,CAAC,CAAA;IAC5B,CAAC;SAAM,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACxC,MAAM,CAAC,uBAAuB,CAAC,CAAA;QAC/B,MAAM,cAAc,CAAC,MAAM,CAAC,CAAA;IAC9B,CAAC;IAED,MAAM,CAAC,4BAA4B,CAAC,CAAA;IACpC,MAAM,KAAK,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAA;AAC5E,CAAC"}
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/create/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAA;AAC7B,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAA;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAGxD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,MAAqB,EAAE,MAA6B;IACjF,MAAM,CAAC,uBAAuB,CAAC,CAAA;IAC/B,MAAM,YAAY,CAAC,MAAM,CAAC,CAAA;IAE1B,MAAM,CAAC,mBAAmB,CAAC,CAAA;IAC3B,IAAI,MAAM,CAAC,OAAO,KAAK,gBAAgB,EAAE,CAAC;QACxC,MAAM,qBAAqB,CAAC,MAAM,CAAC,CAAA;IACrC,CAAC;SAAM,CAAC;QACN,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAA;IAClC,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;QAC/B,MAAM,CAAC,4BAA4B,CAAC,CAAA;QACpC,MAAM,YAAY,CAAC,MAAM,CAAC,CAAA;IAC5B,CAAC;SAAM,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACxC,MAAM,CAAC,uBAAuB,CAAC,CAAA;QAC/B,MAAM,cAAc,CAAC,MAAM,CAAC,CAAA;IAC9B,CAAC;IAED,MAAM,CAAC,4BAA4B,CAAC,CAAA;IACpC,MAAM,KAAK,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAA;AAC5E,CAAC"}
@@ -10,6 +10,7 @@ const CONFIG = {
10
10
  projectName: 'my-app',
11
11
  frontend: 'vite',
12
12
  backend: 'nestjs-rest',
13
+ database: 'postgres',
13
14
  packageManager: 'pnpm',
14
15
  targetDir: '/tmp/my-app',
15
16
  };
@@ -1 +1 @@
1
- {"version":3,"file":"web-vite.test.js","sourceRoot":"","sources":["../../../../src/create/scaffolders/__tests__/web-vite.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAE5D,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,CAAC;IACjC,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;IAC3C,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;IACxC,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC;IACtC,QAAQ,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC;IACvC,SAAS,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;CAChD,CAAC,CAAC,CAAA;AAEH,MAAM,MAAM,GAAG;IACb,WAAW,EAAE,QAAQ;IACrB,QAAQ,EAAE,MAAe;IACzB,OAAO,EAAE,aAAsB;IAC/B,cAAc,EAAE,MAAe;IAC/B,SAAS,EAAE,aAAa;CACzB,CAAA;AAED,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC,CAAA;IAEnC,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;QAC9C,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAA;QAClD,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAA;QAEvD,MAAM,YAAY,CAAC,MAAM,CAAC,CAAA;QAE1B,MAAM,CAAC,KAAK,CAAC,CAAC,oBAAoB,CAChC,MAAM,CAAC,gBAAgB,CAAC,UAAU,CAAC,EACnC,EAAE,SAAS,EAAE,IAAI,EAAE,CACpB,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAA;QAC/C,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAA;QAEvD,MAAM,YAAY,CAAC,MAAM,CAAC,CAAA;QAE1B,MAAM,CAAC,EAAE,CAAC,CAAC,oBAAoB,CAC7B,MAAM,CAAC,gBAAgB,CAAC,UAAU,CAAC,EACnC,MAAM,CAAC,gBAAgB,CAAC,UAAU,CAAC,EACnC,EAAE,SAAS,EAAE,IAAI,EAAE,CACpB,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"web-vite.test.js","sourceRoot":"","sources":["../../../../src/create/scaffolders/__tests__/web-vite.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAE5D,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,CAAC;IACjC,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;IAC3C,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;IACxC,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC;IACtC,QAAQ,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC;IACvC,SAAS,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,SAAS,CAAC;CAChD,CAAC,CAAC,CAAA;AAEH,MAAM,MAAM,GAAG;IACb,WAAW,EAAE,QAAQ;IACrB,QAAQ,EAAE,MAAe;IACzB,OAAO,EAAE,aAAsB;IAC/B,QAAQ,EAAE,UAAmB;IAC7B,cAAc,EAAE,MAAe;IAC/B,SAAS,EAAE,aAAa;CACzB,CAAA;AAED,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC,CAAA;IAEnC,EAAE,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;QAC9C,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAA;QAClD,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAA;QAEvD,MAAM,YAAY,CAAC,MAAM,CAAC,CAAA;QAE1B,MAAM,CAAC,KAAK,CAAC,CAAC,oBAAoB,CAChC,MAAM,CAAC,gBAAgB,CAAC,UAAU,CAAC,EACnC,EAAE,SAAS,EAAE,IAAI,EAAE,CACpB,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAA;QAC/C,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAA;QAEvD,MAAM,YAAY,CAAC,MAAM,CAAC,CAAA;QAE1B,MAAM,CAAC,EAAE,CAAC,CAAC,oBAAoB,CAC7B,MAAM,CAAC,gBAAgB,CAAC,UAAU,CAAC,EACnC,MAAM,CAAC,gBAAgB,CAAC,UAAU,CAAC,EACnC,EAAE,SAAS,EAAE,IAAI,EAAE,CACpB,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { ProjectConfig } from '../types.js';
2
+ export declare function scaffoldNestjsGraphql(config: ProjectConfig): Promise<void>;
3
+ //# sourceMappingURL=api-nestjs-graphql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-nestjs-graphql.d.ts","sourceRoot":"","sources":["../../../src/create/scaffolders/api-nestjs-graphql.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAWhD,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,aAAa,iBAahE"}
@@ -0,0 +1,31 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { copyTemplate } from './utils/copy.js';
5
+ const __dirname = dirname(fileURLToPath(import.meta.url));
6
+ const TEMPLATES_DIR = join(__dirname, '..', '..', '..', 'templates');
7
+ const DB_CONFIG = {
8
+ postgres: { type: 'postgres', port: '5432', driver: 'pg', driverVersion: '^8.0.0', typesPackage: '@types/pg', typesVersion: '^8.0.0' },
9
+ mysql: { type: 'mysql', port: '3306', driver: 'mysql2', driverVersion: '^3.0.0', typesPackage: null, typesVersion: null },
10
+ };
11
+ export async function scaffoldNestjsGraphql(config) {
12
+ const appDir = join(config.targetDir, 'apps', 'backend');
13
+ await mkdir(appDir, { recursive: true });
14
+ const db = DB_CONFIG[config.database];
15
+ await copyTemplate(join(TEMPLATES_DIR, 'api-nestjs-graphql'), appDir, {
16
+ projectName: config.projectName,
17
+ dbType: db.type,
18
+ dbPort: db.port,
19
+ });
20
+ await injectDbDriver(appDir, db);
21
+ }
22
+ async function injectDbDriver(appDir, db) {
23
+ const pkgPath = join(appDir, 'package.json');
24
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));
25
+ pkg.dependencies[db.driver] = db.driverVersion;
26
+ if (db.typesPackage) {
27
+ pkg.devDependencies[db.typesPackage] = db.typesVersion;
28
+ }
29
+ await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
30
+ }
31
+ //# sourceMappingURL=api-nestjs-graphql.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-nestjs-graphql.js","sourceRoot":"","sources":["../../../src/create/scaffolders/api-nestjs-graphql.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AACzD,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,CAAA;AAEpE,MAAM,SAAS,GAAG;IAChB,QAAQ,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE;IACtI,KAAK,EAAK,EAAE,IAAI,EAAE,OAAO,EAAK,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE;CACvH,CAAA;AAEV,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,MAAqB;IAC/D,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,CAAA;IACxD,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAExC,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAErC,MAAM,YAAY,CAAC,IAAI,CAAC,aAAa,EAAE,oBAAoB,CAAC,EAAE,MAAM,EAAE;QACpE,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,MAAM,EAAE,EAAE,CAAC,IAAI;QACf,MAAM,EAAE,EAAE,CAAC,IAAI;KAChB,CAAC,CAAA;IAEF,MAAM,cAAc,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;AAClC,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,MAAc,EAAE,EAA4C;IACxF,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;IAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;IAExD,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAA;IAE9C,IAAI,EAAE,CAAC,YAAY,EAAE,CAAC;QACpB,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,YAAY,CAAA;IACxD,CAAC;IAED,MAAM,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAA;AACxE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"api-nestjs-rest.d.ts","sourceRoot":"","sources":["../../../src/create/scaffolders/api-nestjs-rest.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAMhD,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,aAAa,iBAO7D"}
1
+ {"version":3,"file":"api-nestjs-rest.d.ts","sourceRoot":"","sources":["../../../src/create/scaffolders/api-nestjs-rest.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAWhD,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,aAAa,iBAa7D"}
@@ -1,14 +1,31 @@
1
- import { mkdir } from 'node:fs/promises';
1
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
2
2
  import { dirname, join } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { copyTemplate } from './utils/copy.js';
5
5
  const __dirname = dirname(fileURLToPath(import.meta.url));
6
6
  const TEMPLATES_DIR = join(__dirname, '..', '..', '..', 'templates');
7
+ const DB_CONFIG = {
8
+ postgres: { type: 'postgres', port: '5432', driver: 'pg', driverVersion: '^8.0.0', typesPackage: '@types/pg', typesVersion: '^8.0.0' },
9
+ mysql: { type: 'mysql', port: '3306', driver: 'mysql2', driverVersion: '^3.0.0', typesPackage: null, typesVersion: null },
10
+ };
7
11
  export async function scaffoldNestjsRest(config) {
8
- const appDir = join(config.targetDir, 'apps', 'api');
12
+ const appDir = join(config.targetDir, 'apps', 'backend');
9
13
  await mkdir(appDir, { recursive: true });
14
+ const db = DB_CONFIG[config.database];
10
15
  await copyTemplate(join(TEMPLATES_DIR, 'api-nestjs-rest'), appDir, {
11
16
  projectName: config.projectName,
17
+ dbType: db.type,
18
+ dbPort: db.port,
12
19
  });
20
+ await injectDbDriver(appDir, db);
21
+ }
22
+ async function injectDbDriver(appDir, db) {
23
+ const pkgPath = join(appDir, 'package.json');
24
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));
25
+ pkg.dependencies[db.driver] = db.driverVersion;
26
+ if (db.typesPackage) {
27
+ pkg.devDependencies[db.typesPackage] = db.typesVersion;
28
+ }
29
+ await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
13
30
  }
14
31
  //# sourceMappingURL=api-nestjs-rest.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-nestjs-rest.js","sourceRoot":"","sources":["../../../src/create/scaffolders/api-nestjs-rest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AACzD,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,CAAA;AAEpE,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAqB;IAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;IACpD,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAExC,MAAM,YAAY,CAAC,IAAI,CAAC,aAAa,EAAE,iBAAiB,CAAC,EAAE,MAAM,EAAE;QACjE,WAAW,EAAE,MAAM,CAAC,WAAW;KAChC,CAAC,CAAA;AACJ,CAAC"}
1
+ {"version":3,"file":"api-nestjs-rest.js","sourceRoot":"","sources":["../../../src/create/scaffolders/api-nestjs-rest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAExC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AACzD,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,CAAA;AAEpE,MAAM,SAAS,GAAG;IAChB,QAAQ,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE;IACtI,KAAK,EAAK,EAAE,IAAI,EAAE,OAAO,EAAK,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE;CACvH,CAAA;AAEV,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAqB;IAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,CAAA;IACxD,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAExC,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAErC,MAAM,YAAY,CAAC,IAAI,CAAC,aAAa,EAAE,iBAAiB,CAAC,EAAE,MAAM,EAAE;QACjE,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,MAAM,EAAE,EAAE,CAAC,IAAI;QACf,MAAM,EAAE,EAAE,CAAC,IAAI;KAChB,CAAC,CAAA;IAEF,MAAM,cAAc,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;AAClC,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,MAAc,EAAE,EAA4C;IACxF,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;IAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;IAExD,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAA;IAE9C,IAAI,EAAE,CAAC,YAAY,EAAE,CAAC;QACpB,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,YAAY,CAAA;IACxD,CAAC;IAED,MAAM,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAA;AACxE,CAAC"}
@@ -1,10 +1,12 @@
1
1
  export type PackageManager = 'pnpm' | 'npm';
2
2
  export type Frontend = 'vite' | 'nextjs';
3
- export type Backend = 'nestjs-rest';
3
+ export type Backend = 'nestjs-rest' | 'nestjs-graphql';
4
+ export type Database = 'postgres' | 'mysql';
4
5
  export interface ProjectConfig {
5
6
  projectName: string;
6
7
  frontend: Frontend;
7
8
  backend: Backend;
9
+ database: Database;
8
10
  packageManager: PackageManager;
9
11
  targetDir: string;
10
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/create/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,KAAK,CAAA;AAC3C,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAA;AACxC,MAAM,MAAM,OAAO,GAAG,aAAa,CAAA;AAEnC,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,QAAQ,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;IAChB,cAAc,EAAE,cAAc,CAAA;IAC9B,SAAS,EAAE,MAAM,CAAA;CAClB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/create/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,KAAK,CAAA;AAC3C,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAA;AACxC,MAAM,MAAM,OAAO,GAAG,aAAa,GAAG,gBAAgB,CAAA;AACtD,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAA;AAE3C,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,QAAQ,CAAA;IAClB,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,QAAQ,CAAA;IAClB,cAAc,EAAE,cAAc,CAAA;IAC9B,SAAS,EAAE,MAAM,CAAA;CAClB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exanderal/stackcraft",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Opinionated full-stack project scaffolding CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,4 @@
1
+ {
2
+ "singleQuote": true,
3
+ "trailingComma": "all"
4
+ }
@@ -0,0 +1,34 @@
1
+ // @ts-check
2
+ import eslint from '@eslint/js';
3
+ import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
4
+ import globals from 'globals';
5
+ import tseslint from 'typescript-eslint';
6
+
7
+ export default tseslint.config(
8
+ {
9
+ ignores: ['eslint.config.mjs'],
10
+ },
11
+ eslint.configs.recommended,
12
+ ...tseslint.configs.recommendedTypeChecked,
13
+ eslintPluginPrettierRecommended,
14
+ {
15
+ languageOptions: {
16
+ globals: {
17
+ ...globals.node,
18
+ ...globals.jest,
19
+ },
20
+ sourceType: 'commonjs',
21
+ parserOptions: {
22
+ projectService: true,
23
+ tsconfigRootDir: import.meta.dirname,
24
+ },
25
+ },
26
+ },
27
+ {
28
+ rules: {
29
+ '@typescript-eslint/no-explicit-any': 'off',
30
+ '@typescript-eslint/no-floating-promises': 'warn',
31
+ '@typescript-eslint/no-unsafe-argument': 'warn'
32
+ },
33
+ },
34
+ );
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/nest-cli",
3
+ "collection": "@nestjs/schematics",
4
+ "sourceRoot": "src",
5
+ "compilerOptions": {
6
+ "deleteOutDir": true
7
+ }
8
+ }
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "{{projectName}}-backend",
3
+ "version": "0.0.1",
4
+ "description": "",
5
+ "author": "",
6
+ "private": true,
7
+ "license": "UNLICENSED",
8
+ "scripts": {
9
+ "dev": "nest start --watch",
10
+ "build": "nest build",
11
+ "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
12
+ "start": "nest start",
13
+ "start:dev": "nest start --watch",
14
+ "start:debug": "nest start --debug --watch",
15
+ "start:prod": "node dist/main",
16
+ "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
17
+ "test": "jest",
18
+ "test:watch": "jest --watch",
19
+ "test:cov": "jest --coverage",
20
+ "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
21
+ "test:e2e": "jest --config ./test/jest-e2e.json"
22
+ },
23
+ "dependencies": {
24
+ "@apollo/server": "^4.0.0",
25
+ "@nestjs/apollo": "^13.0.0",
26
+ "@nestjs/common": "^11.0.1",
27
+ "@nestjs/config": "^3.0.0",
28
+ "@nestjs/core": "^11.0.1",
29
+ "@nestjs/graphql": "^13.0.0",
30
+ "@nestjs/platform-express": "^11.0.1",
31
+ "@nestjs/typeorm": "^10.0.0",
32
+ "graphql": "^16.0.0",
33
+ "reflect-metadata": "^0.2.2",
34
+ "rxjs": "^7.8.1",
35
+ "typeorm": "^0.3.0"
36
+ },
37
+ "devDependencies": {
38
+ "@eslint/eslintrc": "^3.2.0",
39
+ "@eslint/js": "^9.18.0",
40
+ "@nestjs/cli": "^11.0.0",
41
+ "@nestjs/schematics": "^11.0.0",
42
+ "@nestjs/testing": "^11.0.1",
43
+ "@swc/cli": "^0.6.0",
44
+ "@swc/core": "^1.10.7",
45
+ "@types/express": "^5.0.0",
46
+ "@types/jest": "^29.5.14",
47
+ "@types/node": "^22.10.7",
48
+ "@types/supertest": "^6.0.2",
49
+ "eslint": "^9.18.0",
50
+ "eslint-config-prettier": "^10.0.1",
51
+ "eslint-plugin-prettier": "^5.2.2",
52
+ "globals": "^16.0.0",
53
+ "jest": "^29.7.0",
54
+ "prettier": "^3.4.2",
55
+ "source-map-support": "^0.5.21",
56
+ "supertest": "^7.0.0",
57
+ "ts-jest": "^29.2.5",
58
+ "ts-loader": "^9.5.2",
59
+ "ts-node": "^10.9.2",
60
+ "tsconfig-paths": "^4.2.0",
61
+ "typescript": "^5.7.3",
62
+ "typescript-eslint": "^8.20.0"
63
+ },
64
+ "jest": {
65
+ "moduleFileExtensions": [
66
+ "js",
67
+ "json",
68
+ "ts"
69
+ ],
70
+ "rootDir": "src",
71
+ "testRegex": ".*\\.spec\\.ts$",
72
+ "transform": {
73
+ "^.+\\.(t|j)s$": "ts-jest"
74
+ },
75
+ "collectCoverageFrom": [
76
+ "**/*.(t|j)s"
77
+ ],
78
+ "coverageDirectory": "../coverage",
79
+ "testEnvironment": "node"
80
+ }
81
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "backend",
3
+ "targets": {
4
+ "dev": {
5
+ "executor": "nx:run-commands",
6
+ "options": { "command": "pnpm run dev", "cwd": "{projectRoot}" }
7
+ },
8
+ "build": {
9
+ "executor": "nx:run-commands",
10
+ "options": { "command": "pnpm run build", "cwd": "{projectRoot}" }
11
+ },
12
+ "lint": {
13
+ "executor": "nx:run-commands",
14
+ "options": { "command": "pnpm run lint", "cwd": "{projectRoot}" }
15
+ },
16
+ "test": {
17
+ "executor": "nx:run-commands",
18
+ "options": { "command": "pnpm run test", "cwd": "{projectRoot}" }
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,21 @@
1
+ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
2
+ import { Module } from '@nestjs/common';
3
+ import { ConfigModule } from '@nestjs/config';
4
+ import { GraphQLModule } from '@nestjs/graphql';
5
+ import { join } from 'node:path';
6
+ import { DatabaseModule } from './modules/database/database.module';
7
+ import { HealthModule } from './modules/health/health.module';
8
+
9
+ @Module({
10
+ imports: [
11
+ ConfigModule.forRoot({ isGlobal: true }),
12
+ GraphQLModule.forRoot<ApolloDriverConfig>({
13
+ driver: ApolloDriver,
14
+ autoSchemaFile: join(process.cwd(), 'src/schema.gql'),
15
+ sortSchema: true,
16
+ }),
17
+ DatabaseModule,
18
+ HealthModule,
19
+ ],
20
+ })
21
+ export class AppModule {}
@@ -0,0 +1,21 @@
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
+ }
@@ -0,0 +1,21 @@
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
+ }
@@ -0,0 +1,22 @@
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
+ }
@@ -0,0 +1,24 @@
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
+ }
@@ -0,0 +1,23 @@
1
+ import { FindManyOptions, FindOptionsWhere } from 'typeorm';
2
+ import { BaseEntity } from '../entities/base.entity';
3
+ import { ReadonlyEntityRepository } from '../repositories/readonly-entity.repository';
4
+
5
+ export abstract class ReadonlyEntityService<T extends BaseEntity> {
6
+ constructor(protected readonly repository: ReadonlyEntityRepository<T>) {}
7
+
8
+ findAll(options?: FindManyOptions<T>): Promise<T[]> {
9
+ return this.repository.findAll(options);
10
+ }
11
+
12
+ findByIds(ids: string[]): Promise<T[]> {
13
+ return this.repository.findByIds(ids);
14
+ }
15
+
16
+ findById(id: string): Promise<T | null> {
17
+ return this.repository.findById(id);
18
+ }
19
+
20
+ findOne(where: FindOptionsWhere<T>): Promise<T | null> {
21
+ return this.repository.findOne(where);
22
+ }
23
+ }