nrwl / nx

Smart Monorepos · Fast CI
https://nx.dev
MIT License
23.78k stars 2.37k forks source link

NestJS generator not working properly #28910

Closed bzp2010 closed 1 week ago

bzp2010 commented 1 week ago

Current Behavior

When using the nx g @nx/nest:service generator, it always reports an error, and the file cannot be generated properly. Check the error log, it seems to indicate that the path value is empty.

Referring to some of the other nestjs generators, it seems to me that it is missing a --path parameter, and if I try to enter it, nx will point out that this parameter is not in the schema.

Indeed, https://github.com/nrwl/nx/blob/master/packages/nest/src/generators/service/schema.json

And, it actually worked when I added path to the jsonschema specification. But this still has some other problems, and I mentioned it at the end of the issue.

Expected Behavior

It should normally generate a typescript file.

GitHub Repo

No response

Steps to Reproduce

  1. pnpm create nx-workspace my-workspace --preset=nest
  2. cd apps/my-workspace/src/app
  3. nx g @nx/nest:service --name auth --verbose

Nx Report

Node           : 20.13.1
OS             : linux-x64
Native Target  : x86_64-linux
npm            : 10.5.2

nx (global)        : 20.1.0
nx                 : 20.1.0
@nx/js             : 20.1.0
@nx/jest           : 20.1.0
@nx/eslint         : 20.1.0
@nx/workspace      : 20.1.0
@nx/eslint-plugin  : 20.1.0
@nx/nest           : 20.1.0
@nx/node           : 20.1.0
@nx/web            : 20.1.0
@nx/webpack        : 20.1.0
typescript         : 5.5.4
---------------------------------------
Registered Plugins:
@nx/webpack/plugin
@nx/eslint/plugin
@nx/jest/plugin

Failure Logs

➜  my-workspace ✗ nx g @nx/nest:service --name auth --verbose

 NX  Generating @nx/nest:service

 NX   Cannot read properties of undefined (reading 'replace')

TypeError: Cannot read properties of undefined (reading 'replace')
    at extractNameAndDirectoryFromPath (/home/ubuntu/code/my-workspace/node_modules/.pnpm/@nx+devkit@20.1.0_nx@20.1.0_@swc-node+register@1.9.2_@swc+core@1.5.29_@swc+helpers@0.5.13__@s_oybkiddel7pp5ztc5anzgdwbvy/node_modules/@nx/devkit/src/generators/artifact-name-and-directory-utils.js:69:17)
    at getNameAndDirectoryOptions (/home/ubuntu/code/my-workspace/node_modules/.pnpm/@nx+devkit@20.1.0_nx@20.1.0_@swc-node+register@1.9.2_@swc+core@1.5.29_@swc+helpers@0.5.13__@s_oybkiddel7pp5ztc5anzgdwbvy/node_modules/@nx/devkit/src/generators/artifact-name-and-directory-utils.js:19:46)
    at determineArtifactNameAndDirectoryOptions (/home/ubuntu/code/my-workspace/node_modules/.pnpm/@nx+devkit@20.1.0_nx@20.1.0_@swc-node+register@1.9.2_@swc+core@1.5.29_@swc+helpers@0.5.13__@s_oybkiddel7pp5ztc5anzgdwbvy/node_modules/@nx/devkit/src/generators/artifact-name-and-directory-utils.js:10:31)
    at normalizeOptions (/home/ubuntu/code/my-workspace/node_modules/.pnpm/@nx+nest@20.1.0_@babel+traverse@7.25.9_@swc-node+register@1.9.2_@swc+core@1.5.29_@swc+helpers_y7gceewjke5qrpusjipsa3rm6y/node_modules/@nx/nest/src/generators/utils/normalize-options.js:7:124)
    at normalizeServiceOptions (/home/ubuntu/code/my-workspace/node_modules/.pnpm/@nx+nest@20.1.0_@babel+traverse@7.25.9_@swc-node+register@1.9.2_@swc+core@1.5.29_@swc+helpers_y7gceewjke5qrpusjipsa3rm6y/node_modules/@nx/nest/src/generators/service/service.js:11:66)
    at serviceGenerator (/home/ubuntu/code/my-workspace/node_modules/.pnpm/@nx+nest@20.1.0_@babel+traverse@7.25.9_@swc-node+register@1.9.2_@swc+core@1.5.29_@swc+helpers_y7gceewjke5qrpusjipsa3rm6y/node_modules/@nx/nest/src/generators/service/service.js:6:27)
    at /home/ubuntu/code/my-workspace/node_modules/.pnpm/nx@20.1.0_@swc-node+register@1.9.2_@swc+core@1.5.29_@swc+helpers@0.5.13__@swc+types@0.1.12_ty_lxgbel7nnuqlogftlxczfb77ne/node_modules/nx/src/command-line/generate/generate.js:240:32
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async handleErrors (/home/ubuntu/code/my-workspace/node_modules/.pnpm/nx@20.1.0_@swc-node+register@1.9.2_@swc+core@1.5.29_@swc+helpers@0.5.13__@swc+types@0.1.12_ty_lxgbel7nnuqlogftlxczfb77ne/node_modules/nx/src/utils/handle-errors.js:8:24)
    at async Object.handler (/home/ubuntu/code/my-workspace/node_modules/.pnpm/nx@20.1.0_@swc-node+register@1.9.2_@swc+core@1.5.29_@swc+helpers@0.5.13__@swc+types@0.1.12_ty_lxgbel7nnuqlogftlxczfb77ne/node_modules/nx/src/command-line/generate/command-object.js:13:22)

Package Manager Version

pnpm 9.9.0

Operating System

Additional Information

In addition to that, I would kindly ask you to indicate the correct way to use the nestjs generator, as it is very different from the way I used it during nx 16-18. I know we've switched to the default as-provided, but it also looks like the nestjs generator doesn't support directory but uses path instead.

I've read https://nx.dev/deprecated/as-provided-vs-derived#generate-paths-and-names-asprovided, but it doesn't look like that's what actually works, which I don't understand.

For example, when I try to generate code for a project in the root directory, I use the --path option and the files are generated in the wrong path.

➜  my-workspace git:(main) ✗ nx g @nx/nest:guard --name auth --path apps/my-workspace/src/app

 NX  Generating @nx/nest:guard

CREATE apps/my-workspace/src/apps/my-workspace/src/app/app.guard.spec.ts
CREATE apps/my-workspace/src/apps/my-workspace/src/app/app.guard.ts

It is duplicated in the apps/my-workspace/src section and I have to move it manually which I think might be wrong, it should be able to work correctly, in the past I used --directory to deal with the problem directly, but now it seems that it no longer exists.

I saw that section included in --path and maybe it was the cause of the error, so I went into that folder and generated it again.

➜  my-workspace git:(main) ✗ cd apps/my-workspace/src 
➜  src git:(main) ✗ nx g @nx/nest:guard --name auth --path app                      

 NX  Generating @nx/nest:guard

CREATE apps/my-workspace/src/app/app.guard.spec.ts
CREATE apps/my-workspace/src/app/app.guard.ts

Now, the generated path is correct, but again, I noticed that the generated file names app.guard.ts and classes AppGuard in them do not follow the input value auth, but appear to be automatically inferred from the path app. It's not just guard, but other nestjs code generators have similar problems, such as controllers and filters.

If I want to make sure the filename is correct, I have to make sure that the last part of the path in --path is the filename prefix I want. Just like:

nx g @nx/nest:guard --name auth --path app/auth
nx g @nx/nest:guard --name auth --path auth

But unfortunately the two methods above fall back into the cycle of problems about paths mentioned above.

➜  src git:(main) ✗ nx g @nx/nest:guard --name auth --path app/auth

 NX  Generating @nx/nest:guard

CREATE apps/my-workspace/src/app/app/auth/auth.guard.spec.ts
CREATE apps/my-workspace/src/app/app/auth/auth.guard.ts

➜  src git:(main) ✗ nx g @nx/nest:guard --name auth --path auth    

 NX  Generating @nx/nest:guard

CREATE apps/my-workspace/src/auth/auth.guard.spec.ts
CREATE apps/my-workspace/src/auth/auth.guard.ts

It looks like I have to enter the app directory to generate the file at auth/auth.guard.ts.

This breaks the intuition of the previous generator's use of --directory, and --path forces me to infer where I should be generating files and return the up-level directory in that location.

Also, I don't want to place the generated files inside a directory, well, at least not every time, e.g. I want to generate auth.guard.ts instead of auth/auth.guard.ts, which, if memory serves me correctly, was the behavior when I used --directory before.

I ask you to show me the right way. Can I generate the code in the root directory by specifying the path? Should the name specified via --name be used as the filename and class name? How to correctly infer --path, or restore --directory behavior?

NorseJedi commented 1 week ago

I'm having the same problems with the paths when using the @nx/nest generators, and I can't really remember a time when this worked properly. I think it worked as you'd expect when you could still use inferred paths, but as far as I can remember it's never worked with explicit paths.

Unless I'm misreading things, you don't actually need to use "--path", but can just specify the path as the first argument to the generator (the schema-file for all the generators I've looked at shows it as defaulting to the first argument you specify). Not that it matters though, it still doesn't work.

Here are some examples I've been struggling with, mostly for the resource generator, but I've tried others (not all though) and the problems seems to be either the same or similar for all of them.

The path I specify has to begin with apps/my-workspace/<something> otherwise it just refuses to accept it.

nx g @nx/nest:resource apps/ --name user --type rest --crud true --dry-run
NX  Generating @nx/nest:resource

NX   The provided directory resolved relative to the current working directory "" does not exist under any project root. Please make sure to navigate to a location or provide a directory that exists under a project root.
nx g @nx/nest:resource apps/my-workspace --name user --type rest --crud true --dry-run
NX  Generating @nx/nest:resource

NX   The provided directory resolved relative to the current working directory "apps" does not exist under any project root. Please make sure to navigate to a location or provide a directory that exists under a project root.

Then when I specify a proper path, it goes ahead and screws it up completely - it's basically seeing double:

nx g @nx/nest:resource apps/my-workspace/src/app/user --name user --type rest --crud true
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/user/user.controller.spec.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/user/user.controller.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/user/user.module.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/user/user.service.spec.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/user/user.service.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/user/dto/create-user.dto.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/user/dto/update-user.dto.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/user/entities/user.entity.ts
UPDATE apps/my-workspace/src/app/app.module.ts
nx g @nx/nest:resource apps/my-workspace/src/user --name user --type rest --crud true
CREATE apps/my-workspace/src/apps/my-workspace/src/user/user.controller.spec.ts
CREATE apps/my-workspace/src/apps/my-workspace/src/user/user.controller.ts
CREATE apps/my-workspace/src/apps/my-workspace/src/user/user.module.ts
CREATE apps/my-workspace/src/apps/my-workspace/src/user/user.service.spec.ts
CREATE apps/my-workspace/src/apps/my-workspace/src/user/user.service.ts
CREATE apps/my-workspace/src/apps/my-workspace/src/user/dto/create-user.dto.ts
CREATE apps/my-workspace/src/apps/my-workspace/src/user/dto/update-user.dto.ts
CREATE apps/my-workspace/src/apps/my-workspace/src/user/entities/user.entity.ts
UPDATE apps/my-workspace/src/app.module.ts
nx g @nx/nest:resource apps/my-workspace/user --name user --type rest --crud true
CREATE apps/my-workspace/apps/my-workspace/user/user.controller.spec.ts
CREATE apps/my-workspace/apps/my-workspace/user/user.controller.ts
CREATE apps/my-workspace/apps/my-workspace/user/user.module.ts
CREATE apps/my-workspace/apps/my-workspace/user/user.service.spec.ts
CREATE apps/my-workspace/apps/my-workspace/user/user.service.ts
CREATE apps/my-workspace/apps/my-workspace/user/dto/create-user.dto.ts
CREATE apps/my-workspace/apps/my-workspace/user/dto/update-user.dto.ts
CREATE apps/my-workspace/apps/my-workspace/user/entities/user.entity.ts
UPDATE apps/my-workspace/app.module.ts

Also, it doesn't seem to care about the name as much as you'd expect. If I don't put it in the path, it'll ignore it:

nx g @nx/nest:resource apps/my-workspace/src/app --name user --type rest --crud true
CREATE apps/my-workspace/src/apps/my-workspace/src/app/app.controller.spec.ts
CREATE apps/my-workspace/src/apps/my-workspace/src/app/app.controller.ts
CREATE apps/my-workspace/src/apps/my-workspace/src/app/app.module.ts
CREATE apps/my-workspace/src/apps/my-workspace/src/app/app.service.spec.ts
CREATE apps/my-workspace/src/apps/my-workspace/src/app/app.service.ts
CREATE apps/my-workspace/src/apps/my-workspace/src/app/dto/create-user.dto.ts
CREATE apps/my-workspace/src/apps/my-workspace/src/app/dto/update-user.dto.ts
CREATE apps/my-workspace/src/apps/my-workspace/src/app/entities/user.entity.ts

If I put a different directory name than the name of the resource, it'll also ignore the name and go with the directory as name.

nx g @nx/nest:resource apps/my-workspace/src/app/fubar --name user --type rest --crud true

NX  Generating @nx/nest:resource

CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/fubar/fubar.controller.spec.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/fubar/fubar.controller.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/fubar/fubar.module.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/fubar/fubar.service.spec.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/fubar/fubar.service.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/fubar/dto/create-fubar.dto.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/fubar/dto/update-fubar.dto.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/fubar/entities/fubar.entity.ts
UPDATE package.json
UPDATE apps/my-workspace/src/app/app.module.ts

If I don't specify the name at all, it will ask me to specify it, and then it goes ahead and ignores it completely:

nx g @nx/nest:resource apps/my-workspace/src/app/user --type rest --crud true

NX  Generating @nx/nest:resource

What name would you like to use for this resource (plural, e.g., `users`)? · fubar
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/user/user.controller.spec.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/user/user.controller.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/user/user.module.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/user/user.service.spec.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/user/user.service.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/user/dto/create-user.dto.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/user/dto/update-user.dto.ts
CREATE apps/my-workspace/src/app/apps/my-workspace/src/app/user/entities/user.entity.ts
UPDATE package.json
UPDATE apps/my-workspace/src/app/app.module.ts

Lastly, if I also don't specify the final directory and don't specify the name, it'll still ask me to specify the name, and then it dies horribly:

nx g @nx/nest:resource apps/my-workspace/src/app/ --type rest --crud true

NX  Generating @nx/nest:resource

What name would you like to use for this resource (plural, e.g., `users`)? · user
InvalidInputOptions [Error]: Schematic input does not validate against the Schema: {"type":"rest","crud":true,"path":"apps/my-workspace/src/app/","name":"app","unitTestRunner":"jest","skipImport":false,"flat":true,"sourceRoot":"apps/my-workspace/src","spec":true}
Errors:

  Data path "/path" must match format "path".
    at /home/roy/tmp/my-workspace/node_modules/.pnpm/@angular-devkit+schematics@17.3.11_chokidar@3.6.0/node_modules/@angular-devkit/schematics/tools/schema-option-transform.js:30:27
    at /home/roy/tmp/my-workspace/node_modules/.pnpm/rxjs@7.8.1/node_modules/rxjs/dist/cjs/internal/operators/map.js:10:37
    at OperatorSubscriber._this._next (/home/roy/tmp/my-workspace/node_modules/.pnpm/rxjs@7.8.1/node_modules/rxjs/dist/cjs/internal/operators/OperatorSubscriber.js:33:21)
    at Subscriber.next (/home/roy/tmp/my-workspace/node_modules/.pnpm/rxjs@7.8.1/node_modules/rxjs/dist/cjs/internal/Subscriber.js:51:18)
    at /home/roy/tmp/my-workspace/node_modules/.pnpm/rxjs@7.8.1/node_modules/rxjs/dist/cjs/internal/operators/throwIfEmpty.js:13:24
    at OperatorSubscriber._this._next (/home/roy/tmp/my-workspace/node_modules/.pnpm/rxjs@7.8.1/node_modules/rxjs/dist/cjs/internal/operators/OperatorSubscriber.js:33:21)
    at Subscriber.next (/home/roy/tmp/my-workspace/node_modules/.pnpm/rxjs@7.8.1/node_modules/rxjs/dist/cjs/internal/Subscriber.js:51:18)
    at /home/roy/tmp/my-workspace/node_modules/.pnpm/rxjs@7.8.1/node_modules/rxjs/dist/cjs/internal/operators/take.js:15:32
    at OperatorSubscriber._this._next (/home/roy/tmp/my-workspace/node_modules/.pnpm/rxjs@7.8.1/node_modules/rxjs/dist/cjs/internal/operators/OperatorSubscriber.js:33:21)
    at Subscriber.next (/home/roy/tmp/my-workspace/node_modules/.pnpm/rxjs@7.8.1/node_modules/rxjs/dist/cjs/internal/Subscriber.js:51:18) {
  errors: [
    {
      instancePath: '/path',
      schemaPath: '#/properties/path/format',
      keyword: 'format',
      params: [Object],
      message: 'must match format "path"'
    }
  ]
}

NX   Schematic input does not validate against the Schema: {"type":"rest","crud":true,"path":"apps/my-workspace/src/app/","name":"app","unitTestRunner":"jest","skipImport":false,"flat":true,"sourceRoot":"apps/my-workspace/src","spec":true}

Errors:

  Data path "/path" must match format "path".
Pass --verbose to see the stacktrace.
bzp2010 commented 1 week ago

Hi, @NorseJedi. I encountered all of the problems you mentioned, and it's frustrating.

It doesn't matter that you don't remember a time when it was able to work properly, I still remember it, at least on the nx 19, and it still works.

In one of my old repositories I was still stay on nx 19 until I created an experimental project using nx 20 and everything broke.

nx --version

Nx Version:
- Local: v19.3.0
- Global: v20.1.0

Back then, I used commands like:

nx g @nx/nest:guard --name auth --directory apps/[hidden]/src/app/config

 NX  Generating @nx/nest:guard

? Where should the guard be generated? … 
▸ As provided: apps/[hidden]/src/app/config/auth.ts
  Derived:     apps/[hidden]/src/apps/[hidden]/src/app/config/auth.ts

Of course, I'm using the As-provided pattern, so it generates:

CREATE apps/[hidden]/src/app/config/auth.guard.spec.ts
CREATE apps/[hidden]/src/app/config/auth.guard.ts

No misplaced paths, no duplicate paths, no weird nested folders, everything is fine. This is exactly what I expected, nothing that forces me to have to guess and infer paths, and the generated files are not nested by their folder prefixes. The files are generated in exactly the folder I specify, which is fairly intuitive.

NorseJedi commented 1 week ago

@bzp2010 You're right, it did work in v19, and also in v18 (I just tested them both). I clearly misremembered, I just remember I struggled a lot with getthing this right for both NestJS and Angular, but now I can't figure out why that was...

Anyway, it's still broken in v20, and hopefully it's an easy thing to fix :)

pawel-twardziak commented 1 week ago

Hi @NorseJedi @bzp2010 yeah, that's easy peazy 👍 Will arrive in around 1-2 hours

bzp2010 commented 1 week ago

@pawel-twardziak @ndcunningham Thanks for the quick fix. 🫡

But I still have questions about the generators mentioned in the issue Additional Information section, can anyone clarify the correct way to use the nestjs generator? Or do I need to start a new issue on this?

pawel-twardziak commented 1 week ago

Hi @bzp2010, let me go through the Additional Information and I will answer this weekend.

pawel-twardziak commented 1 week ago

I am not it @bzp2010 :) might be I will answer your question tomorrow cause I have had a bit lazy weekend - need to rest from my computer a bit. If not today late evening, than tomorrow till the end of the day.

pawel-twardziak commented 1 week ago

Hi @bzp2010 👋 sorry for keeping you waiting. It seems like you are right and the code generators for nestjs work weird (apparently the generators and the docs don't match) - create a spearate issue please and I will work on it (if only I could get mentioned there 😆). You can simply ctrl-c/ctrl-v the Additional Information section.

And thank you @bzp2010 for your vigilance ❤