Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nuxt module dependencies are not inherited from layers #26980

Closed
Brads3290 opened this issue Apr 28, 2024 · 17 comments
Closed

Nuxt module dependencies are not inherited from layers #26980

Brads3290 opened this issue Apr 28, 2024 · 17 comments

Comments

@Brads3290
Copy link

Brads3290 commented Apr 28, 2024

Environment

  • Operating System: Darwin
  • Node Version: v20.11.1
  • Nuxt Version: -
  • CLI Version: 3.11.1
  • Nitro Version: -
  • Package Manager: yarn@1.22.15
  • Builder: -
  • User Config: -
  • Runtime Modules: -
  • Build Modules: -

Reproduction

https://github.com/Brads3290/nuxt-layer-devependency-repro/tree/repro

Describe the bug

UPDATE: I incorrectly assumed this is an issue with dev dependencies, but as @adamdehaven pointed out, "dev dependencies not being inherited" is expected. The actual issue is that modules used by the layer are not inherited by the main app. This happens even when they are specified as runtime dependencies in the layer.


When using a Nuxt layer, dev dependencies are not installed in the consumer of the layer (e.g. the main app).

Copied my explanation from the repro README here. I've provided a branch (working) with a working example of inheriting layer dependencies (where runtime dependencies are inherited), and another branch (repro) where we attempt to inherit dev dependencies (the tailwindcss Nuxt module), but they aren't inherited by the app.

## Branch: `working`
Working example

The `working` branch is a working example of the layer having a runtime dependency (https://github.com/unjs/ufo) and working fine.
- `nuxt-layer` depends on, and uses, unjs/ufo
- Running `npm install` on `nuxt-app` installs `ufo` in `nuxt-app` (even though `nuxt-app` does not directly depend on it, but the layer does), and everything works.

## Branch: `repro`
Bug repro

The `repro` branch is an example of dev dependencies **NOT** following the same behaviour.
- `nuxt-layer` depends on, and uses, `@nuxt/tailwindcss` as a **dev dependency**
- Running `npm install` on `nuxt-app` fails.
- Adding the dev dependency to `nuxt-app` makes everything work again:

// package.json
"devDependencies": {
    "@nuxtjs/tailwindcss": "^6.12.0"
}

Additional context

No response

Logs

No response

@adamdehaven
Copy link
Contributor

Isn't this behavior expected?

Make sure any dependency imported in the layer is explicitly added to the dependencies. The nuxt dependency, and anything only used for testing the layer before publishing, should remain in the devDependencies field.
Source

So in your layer, you would install anything that is needed by the host project as a dependency. Only nuxt, and any testing libs should be installed as devDependencies in your layer.

@Brads3290
Copy link
Author

Brads3290 commented Apr 28, 2024

@adamdehaven Yeah, true it might be intentional, but I think some discussion is required on how to handle cases where dev dependencies actually do need to be inherited by the main app - for example, Nuxt modules.

In my repro, the @nuxt/tailwindcss module is the dev dependency on the layer, and that makes the layer unusable without also manually adding that devDependency to the main app. If you don't add the devDependency to the main app, npm install no longer works on the main app, and running the server doesn't work either - because it can't find @nuxt/tailwindcss.

This seems to be a recurring issue with other Nuxt modules as well (here, here, and here, all these instances, people are trying to use modules), so I don't think it's an issue with the tailwind module specifically.

Clearly this is a case where some dev dependencies are necessary, but others might be undesirable. I'm not really sure how we would differentiate, to be honest. Unless we said "modules are special, modules are inherited as dev dependencies but all other dev dependencies are not"?

Just to completely address your comment here:

So in your layer, you would install anything that is needed by the host project as a dependency. Only nuxt, and any testing libs should be installed as devDependencies in your layer.

Adding @nuxt/tailwindcss to devDependencies (rather than dependencies) is done by Nuxi itself, when you do npx nuxi@latest module add tailwindcss.

Maybe I jumped the gun calling this a "bug", but I think it needs discussion, because right now, it seems installing modules in a layer will make that layer unusable.

@adamdehaven
Copy link
Contributor

adamdehaven commented Apr 28, 2024

Adding @nuxt/tailwindcss to devDependencies (rather than dependencies) is done by Nuxi itself, when you do npx nuxi@latest module add tailwindcss

As I understand them, most of the nuxi commands are geared towards adding dependencies to a host Nuxt application, not specifically for a project that is essentially a Nuxt layer. If your layer requires a module, that module must be a dependency of your layer.

IMO, this falls on the layer author to know that they are building a layer rather than an app, and to have the dependencies specified accordingly (meaning moving any required packages from devDependencies to dependencies).

This follows the same pattern as using any other npm package: if your package has required dependencies, they must either be declared as dependencies or peerDependencies depending on your strategy.

In my repro, the @nuxt/tailwindcss module is the dev dependency on the layer

Simply moving @nuxt/tailwindcss to dependencies will resolve your issue; and if the host app has multiple versions of the tailwind module being imported, it will either 1) auto-resolve, or 2) you can override to resolve to a specific version.

@Brads3290
Copy link
Author

Brads3290 commented Apr 28, 2024

@adamdehaven

Simply moving @nuxt/tailwindcss to dependencies will resolve your issue

Doesn't seem to work I'm afraid, unless I set it up wrong? Repro'd here - https://github.com/Brads3290/nuxt-layer-devependency-repro/tree/repro-runtimedep.

Here's what I did:

  1. Moved @nuxt/tailwindcss from devDependencies to dependencies in nuxt-layer
  2. Run npm install in nuxt-app

Result:

ERROR  Error while requiring module @nuxtjs/tailwindcss: Error: Cannot find module '/Users/brad/Desktop/nuxt-layer-repro/nuxt-app/@nuxtjs/tailwindcss'                                   9:13:04 AM
Require stack:
- /Users/brad/Desktop/nuxt-layer-repro/nuxt-app/index.js

I did also try the same with peerDependencies and it didn't work.

I'll admit that I'm just getting the hang of the npm ecosystem (I'm a .NET guy myself), so there may be something simple I'm missing here - hopefully not, otherwise I've wasted everyone's time.

@adamdehaven
Copy link
Contributor

I only see a lock file in app directory, meaning it doesn't look like you installed dependencies for your layer directory?

If you're treating each directory as a standalone "package" you'll need to install dependencies in both.

The tailwind module isn't anywhere to be found in the lock file in the app directory.

@Brads3290
Copy link
Author

Brads3290 commented Apr 29, 2024

@adamdehaven ah, the package-lock.json is gitignored in the layer directory but not the app directory, for some reason. Must be a difference in the default templates. I have npm install'd in nuxt-layer, and the install still fails in nuxt-app.

If you're treating each directory as a standalone "package" you'll need to install dependencies in both.

On that note, that seems to differ from the behaviour I observed in the working example for traditional runtime dependencies (e.g. unjs/ufo is also a dependency of my repro layer). I specifically tested whether I could npm install in nuxt-app without first doing it in nuxt-layer. I found that I didn't need to do it in nuxt-layer, unjs/ufo just got automatically picked up and installed in nuxt-app when I ran npm install in there.

The tailwind module isn't anywhere to be found in the lock file in the app directory.

I'd say this is exactly what the repro demonstrates - the tailwind module isn't being included in the app, despite it being a dependency of a layer that the app extends.

@adamdehaven
Copy link
Contributor

Your project is working correctly, but you need to follow the instructions I suggested above 😄

Since your nuxt-layer directory is the only code in the repo that has a dependency on the Tailwind module, you have to run the install command there in order to make it available in the host app. This wouldn't be necessary if you packaged your layer as a standalone package and installed it into the host app (instead of just linking to the relative folder).

# cd into the layer directory
cd nuxt-layer

# install dependencies
npm install

# cd back out to the app directory
cd ../nuxt-app

# install dependencies
npm install

# run the app dev server
npm run dev
tailwind running successfully

As you can see from the screenshot above, after following these steps, everything is working as expected.

On that note, that seems to differ from the behaviour I observed in the working example for traditional runtime dependencies (e.g. unjs/ufo is also a dependency of my repro layer). I specifically tested whether I could npm install in nuxt-app without first doing it in nuxt-layer. I found that I didn't need to do it in nuxt-layer, unjs/ufo just got automatically picked up and installed in nuxt-app when I ran npm install in there.

The reason unjs/ufo isn't throwing an error is because it's already a dependency of something else already utilized in the nuxt-app directory, meaning it's already available.

You can run npm why ufo and see that it's a requirement of the nuxt-app project regardless if your layer is used.

@Brads3290
Copy link
Author

@adamdehaven I can't replicate those results, and to the best of my knowledge, I did exactly as you suggested. Which branch of my repository did you use? I linked to the repro branch because that's where the issue is. I also have a branch that demonstrates the working behaviour (using the ufo package). I believe master is also "working".

If there was any confusion there, that's my fault for confusing things with multiple branches - sorry about that.

Here's my terminal, in a completely fresh directory so I'm working from the same code as you (except I suspect you might not have been on the same branch). Some output omitted for brevity.

# Clone the repro branch specifically, which demonstrates the issue
brad@BRAD-LAPTOP-M1 /tmp % git clone -b repro-runtimedep https://github.com/Brads3290/nuxt-layer-devependency-repro.git
Cloning into 'nuxt-layer-devependency-repro'...
    [...]
brad@BRAD-LAPTOP-M1 /tmp % cd nuxt-layer-devependency-repro 
brad@BRAD-LAPTOP-M1 nuxt-layer-devependency-repro % cd nuxt-layer
brad@BRAD-LAPTOP-M1 nuxt-layer % npm install
    [...]
added 994 packages, and audited 996 packages in 7s
    [...]
brad@BRAD-LAPTOP-M1 nuxt-layer % cd ../nuxt-app
brad@BRAD-LAPTOP-M1 nuxt-app % npm install
    [...]
 WARN  Cannot extend config from {"install":true} in /private/tmp/nuxt-layer-devependency-repro/nuxt-app                             10:29:11 am

[10:29:11 am]  ERROR  Error while requiring module @nuxtjs/tailwindcss: Error: Cannot find module '/private/tmp/nuxt-layer-devependency-repro/nuxt-app/@nuxtjs/tailwindcss'
Require stack:
- /private/tmp/nuxt-layer-devependency-repro/nuxt-app/index.js
    [...(the rest of the error)]
npm ERR! A complete log of this run can be found in: /Users/brad/.npm/_logs/2024-04-29T00_29_08_119Z-debug-0.log
brad@BRAD-LAPTOP-M1 nuxt-app % 

Note in my git clone command I used the repro-runtimedep branch instead of repro as repro-runtimedep adheres to your suggestion about putting the tailwind module in dependencies rather than devDependencies. I also ran the same set of commands but with the repro branch, and got the same result.

Good point about unjs/ufo already being used by nuxt-app - I probably shouldn't have picked an unjs package as my test for a runtime dependency.

@adamdehaven
Copy link
Contributor

adamdehaven commented Apr 29, 2024

I believe the issue you're running into now (on the repro-runtimedep branch) appears to be a misconfiguration in the way you have set up the repository, and therefore the dependencies are not properly resolving.

If your layer was truly a separately published package, I don't believe you'd be having the issue (but maybe @manniL or someone can confirm my suspicion).

If you properly set up workspaces in your repository (since you're treating each directory as a standalone package) the dependencies will properly resolve and you should be able to run the Nuxt app without issue.

  1. Create a package.json in the root of the repository with this contents:
{
  "name": "project-name",
  "workspaces": [
    "nuxt-app",
    "nuxt-layer"
  ]
}
  1. Delete any nested node_modules folders in the project, as well as any nested package-lock.json files.
  2. From the root of the repository, run npm install
  3. Once dependencies are installed, run npm run dev --workspace=nuxt-app

Your project should now run successfully (except there's an unrelated issue described below).


Separately, I see you have this install option defined in your nuxt-app/nuxt.config.ts which will prevent the layer from being extended:

WARN  Cannot extend config from {"install":true} in /Users/adam/Desktop/nuxt-layer-devependency-repro/nuxt-app

Since the layer is not a git remote source, you need to exclude the install option:

extends: ['../nuxt-layer']

@Brads3290
Copy link
Author

Brads3290 commented Apr 29, 2024

@adamdehaven Yes! That seems to work!

Thank you very much for your patience, I really appreciate it.

So I guess my question now is - is that something you'd expect someone to know, if they're familiar with npm? In other words, is my unfamiliarity with the environment the root cause here?

I'm just wondering if there's a way we can make this a little easier for someone coming along next time. From my perspective, the situation kind of went like this:

  1. Read through the Nuxt docs, find out about layers, and modules.
  2. Decide layers sound like a good candidate for splitting out my common pages and components in the project I'm starting
  3. Try to use tailwind on those components (with basically out-of-the-box support via the tailwindcss module)
  4. Run head first into this issue.

I feel like I looked at a fair bit of documentation and examples before diving into this (e.g. the Nuxt layers documentation, which also links to this video, also this, and @manniL's video here), and nothing really indicated that in order to use modules with layers, they had to be part of a workspace. The closest was @manniL's video which defined a workspace file, but had no mention of it's relevance - he just used it to show the structure of his project (or at least, that's how I interpreted it).

To me, layers seemed to be an "it just works" way of extracting common parts of a Nuxt app into reusable parts, and there's no way I would have figured out that they needed to be part of a workspace if it wasn't for your help. All the examples seem to treat local layers as "just another directory".

All that to say, from my perspective I think this needs to be documented a bit better. What do you think?

@Brads3290
Copy link
Author

Brads3290 commented Apr 29, 2024

A couple of other thoughts/observations:

  • With this method it seems that you don't need to move the module to dependencies. It will work fine as a dev dependency, presumably because all the dependencies are just bunched together in the one node_modules which makes inheriting dependencies from layers completely redundant.
  • I'm not sure this will work for published layers? Seems they also experience the same problem with modules (see Error when extending layer from GitHub with install: true #26479 from @MuhammadM1998)

Edit: I also checked that Muhammad added @nuxt/ui to dependencies in his layer, and he has.

@adamdehaven
Copy link
Contributor

So I guess my question now is - is that something you'd expect someone to know, if they're familiar with npm? In other words, is my unfamiliarity with the environment the root cause here?

Layers are essentially reusable "things" across projects - this is the piece I think you got confused on. From the docs:

Nuxt layers are a powerful feature that you can use to share and reuse partial Nuxt applications within a monorepo, or from a git repository or npm package.

You were treating your nuxt-layer and nuxt-app directories as separate projects; however, the repository was not properly set up as a monorepo (e.g. npm workspaces) and therefore did not allow the dependencies to be properly resolved. I think this was possibly due to your unfamiliarity with the environment.

If you reference the quote from the docs above:

  1. You kind of had your repository set up as a monorepo; however, since there wasn't a root package.json and workspaces setup in place to manage dependencies, they were not going to resolve properly in the project. A proper monorepo would have the Nuxt Layer as one "workspace" (or package), and the Nuxt app as another. This way, each is treated separately, and the workspace itself is in place to manage the dependencies between the different workspaces.
  2. Had you published the standalone nuxt-layer with the @nuxtjs/tailwindcss module as a dependency as a standalone npm package, and then added that npm package as a dependency of the nuxt-app (or git reference to the extends array), again, it would have worked out of the box.

To me, layers seemed to be an "it just works" way of extracting common parts of a Nuxt app into reusable parts

This is true; however, you also have to properly "prepare" and consume the layers.


  • With this method it seems that you don't need to move the module to dependencies. It will work fine as a dev dependency, presumably because all the dependencies are just bunched together in the one node_modules which makes inheriting dependencies from layers completely redundant.

This is actually incorrect as your layer package would never work in an external repository (its dependencies would not be installed in the host app). Also, your assumption would only be accurate for your local project. If you were to try to attempt to deploy your nuxt-app directory, for example in a GitHub Actions workflow, you would have to be careful to install dependencies for the entire repository, not just the directory you want to deploy.

I use a similar layer structure in several projects and the published, standalone layers do work correctly 😄

@Brads3290 Brads3290 changed the title Layer dev dependencies (including Nuxt modules) are not installed in the main app Nuxt module dependencies are not inherited from layers Apr 29, 2024
@Brads3290
Copy link
Author

Brads3290 commented Apr 29, 2024

@adamdehaven

Ok, thanks again for your help with this. Noted on all points above.

Nuxt layers are a powerful feature that you can use to share and reuse partial Nuxt applications within a monorepo, ...

I think monorepo was the key word I missed there, because I didn't realise that with npm you need to set up a monorepo in a specific way -- using workspaces. I just assumed it was a repo containing all your packages - assumptions, assumptions 🤦‍♂️

Do you think that's worth spelling out a bit more in the docs? For someone coming in from another stack, that's very easy to miss, but maybe it's ubiquitous amongst npm users?

I use a similar layer structure in several projects and the published, standalone layers do work correctly 😄

Ok, good to know - thanks! I might have a look at Muhammad's repro and see if I can figure out where it deviates from what you've said, just to make sure my understanding is solid.

I think this issue can be closed, but could you please let me know if you think it's worth opening a documentation request? I'd be happy to submit a PR to add this info to the docs if they're open sourced.

@Brads3290
Copy link
Author

@adamdehaven

I use a similar layer structure in several projects and the published, standalone layers do work correctly 😄

Just wondering - do you host your layers on GitHub, or npm? I can get external layers to work via npm, but for the life of me I can't get them to work via GitHub if they have module dependencies!

@jbiddulph
Copy link

Hi @Brads3290 have you managed to find a workaround for this yet? I don't want to go the monorepo route

@Brads3290
Copy link
Author

@jbiddulph I didn't, I'm sorry.

I can get it to work with monorepo and with external npm packages, but not with GitHub. I closed this issue since the original question is answered, but gave up getting GitHub dependencies to install.

@davidstackio
Copy link
Contributor

@jbiddulph and @Brads3290 Perhaps it was because of this bug? #26479

Looks like the fix is merged, but has yet to be released. Looks like it will be in v3.12.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants