DEV Community

Alvaro Saburido
Alvaro Saburido

Posted on

Deploying a Nuxt Site on Netlify between a pnpm monorepo

I've been walking through the fine line between over-engineering and learning new integration possibilities with my portfolio crafting.

Because, yes I'm one of those developers who like to complicate his life for the bigger purpose of learning and "probably" implementing this knowledge in future projects.

Overengineering a napkin

Today's articles explain how to successfully deploy a static site with Nuxt v3.0.0 between a pnpm monorepo on Netlify.

For this tutorial, I'm assuming you have a basic understanding of monorepos, npm packages, deploying apps to Netlify, and a little bit of CI/CD. If this list sounded like phrases coming from a Chewbacca to you, consider investigating a little before coming back here to properly get some value out of it.

It sounds very specific, but if you find yourself in the same use-case, this is the article for you.

You can find the code for this repo here https://github.com/alvarosabu/pnpm-monorepo-nuxt

The initial idea

Before you think I'm crazy, this is what I wanted to achieve:

Monorepo structure

Sounds simple, a packages folder containing different libs such as a UI library with my design system done in VueJS, a Typescript lib with utilities and Portfolio website below an apps directory made with Nuxt v3 that will consume several resources from the packages between the monorepo.

I liked the idea of being able to create the UI and utils packages on the road and test them directly on the portfolio without the need for extra playgrounds. Then being able to publish and release npm packages from them to install it in other projects easily.

If you are not planning to publish multiple packages (public or private) then a monorepo might not be the answer for you. A simple repo with a good structure would be enough.

 Choosing PNPM workspaces

At the moment I started to transform this draft of an idea into an actual thing, and did some investigation on which monorepo technologies would suit me the most.

I started with Lerna in the early phases, I was using it with yarn workspaces and the whole CI/CD process was pretty smooth using Semantic Release.

On the road, I discover pnpm as a faster alternative to npm/yarn, also several of my favorite OSS projects were using it (Vite, Vueuse, etc), I decided to give it a try.

Then Lerna announced its temporally archive due to lack of maintenance this year by its lead maintainer, later to being taken over stewardship by Nrwl, check the announcement here

Image description

Before that announcement, several users of Lerna (including me) were looking for an alternative that allowed us to easily create and maintain a monorepo of these characteristics.

Since I was using pnpm, I found out that it has a built-in support for monorepos by creating a Workspace. There it was the solution that I needed.

Pnpm workspace

All you need to do is add a pnpm-workspace.yaml with the following content:

packages:
  # all packages in subdirs of packages/ and app/
  - 'packages/**'
  - 'apps/**'
Enter fullscreen mode Exit fullscreen mode

And tweak your package.json to add this:

// package.json

{
  ...
  "workspaces": [
    "apps/**",
    "packages/*"
  ],
  ...
}
Enter fullscreen mode Exit fullscreen mode

We're ready to move to the next step

 Nuxt

Nuxt3

Being a VueJS and Nuxt fanboy as I'm 🤪, jokes aside, is the framework find myself enjoying the most when coding. It wasn't a difficult choice for me to do my Portfolio with a recently stable Nuxt v3.0.0-rc.

For those of you that are not familiar with The Nuxt ecosystem, is a hybrid Vue framework that allows you to easily create SSR, CSR or SSG experiences with an amazing DX (Developer Experience)

To get started just run this command inside of the apps directory:

pnpm dlx nuxi init portfolio
Enter fullscreen mode Exit fullscreen mode

When installing the dependencies, is important to use the shamefully-hoist flag. (I don't really fully understand why, better explained here)

pnpm install --shamefully-hoist
Enter fullscreen mode Exit fullscreen mode

And voilá, by running pnpm run dev this beautiful welcome screen showed up in my browser:

Nuxt Welcome Page

Before we jump into the Netlify config there is a final thing we need to set on our brand new Nuxt. Since I want to generate the project statically I will use nuxt generate

 Netlify

This is the part where things start to get interesting 🤭.

Netlify is a cloud computing platform that offers hosting and serverless backend services for web apps and static websites.

I personally use it a lot and to be completely fair, I have never used it for such complex integration. Most of the time was to deploy sites for clients, one or another reproduction, slides for a conference talk, some OSS libraries documentation under my domain, you know, easy stuff.

So following the Netlify's documentation for monorepos we can add a netlify.toml file on the root of the repository with the [build].base pointing to the portfolio directory apps/blog like this:

[build.environment]
  NODE_VERSION = "16"
[build]
  base = "apps/blog/"
Enter fullscreen mode Exit fullscreen mode

Then we need a publish directory so Netlify knows where the deploy-ready HTML files and assets generated by the build are located.

Whenever we run nuxt generate, it will prompt the following:

Nuxt generate output

It seems we found our directory .output/public, we can update the netlify.toml to:

[build.environment]
  NODE_VERSION = "16"
[build]
  base = "apps/blog/"
  publish = ".output/public"
Enter fullscreen mode Exit fullscreen mode

🤔 That should work right?

The next thing to set is the build command. This was the moment I realized that Netlify does not support PNPM right away yet (at the time of writing there is a Feature Request open).

🥲 The world seemed to collapse in front of my eyes (drama), Oh no! I will need to get back to yarn, or even worse, to bare npm 😭 (more drama).

I stopped and thought, what would my heroes do at this moment?

My Vite hero

Yeeeess!! Let see how Vite netlify config for docs was looking like and BAAAM 💥 there was the answer to our needs:

[build.environment]
  NODE_VERSION = "16"
  NPM_FLAGS = "--version" # prevent Netlify npm install
[build]
  base = "apps/blog/"
  publish = ".output/public"
  command = "npx pnpm i --store=node_modules/.pnpm-store --frozen-lockfile && npx pnpm run generate"
Enter fullscreen mode Exit fullscreen mode

We added a NPM_FLAGS property to prevent Netlify to install packages with npm by default. Then we use npx to install pnpm and then run any command that we want.

And don't forget the redirects:

[[redirects]]
  from = "/*"
  to = "index.html"
  status = 200
Enter fullscreen mode Exit fullscreen mode

Let's see what happens... 🤔

Image description

If we push and check the deployment on Netlify, we will see that is complaining about Failed to resolve entry for package "@pnpm-monorepo-nuxt/ui".

Let's see how to solve all the different troubles that can happen.

 Trouble Shooting

 Building the UI package

As the log above explains, our blog app is not able to resolve the UI package. So we will need to build it before we generate the blog.

On our apps/blog/package.json we can add a new script called generate:ci

//package.json
  "scripts": {
    "generate:ci": "pnpm run -w build:ui && nuxt generate",
  }
Enter fullscreen mode Exit fullscreen mode

Notice the use of pnpm run with a -w flag, this allows to run the command from the root.

Now let's add that command to our root package.json like this:

"scripts": {
    "build:ui": "pnpm --dir packages/ui build",
  }
Enter fullscreen mode Exit fullscreen mode

Last step will be update the command on the netlify.toml to this new generate:ci

[build]
  base = "apps/blog/"
  publish = ".output/public"
  command = "npx pnpm i --store=node_modules/.pnpm-store --frozen-lockfile && npx pnpm run generate:ci"
Enter fullscreen mode Exit fullscreen mode

Annnnnnnnnnnnnnnd.

Build failed again

#"@%·" ok Alvaro, don't fall into eternal down-spiral of darkness... just breath.

 Getting the Publish directory right

Build failed because of publish directory

Gotta admit this part was a little frustrating for me. Following what Nuxt prompt when using nuxt generate the static files to deploy would be inside .output/public right?.

At this point, I was not able to find any real-life example of a Nuxt3 SSG deployed between a pnpm monorepo. All the references on internet were pretty simple, a Nuxt3 app on a simple repo, just push a Nitro preset will do the magic for you, all worked fine.

Why it was failing in this specific case? I search Netlify Monorepo Docs, Nuxt3 docs about deployment and I couldn't find any clue.

I tried every combination of things you can imagine, went to the multiverse and back, changing the base directory, the commands, everything.

Doctor Strange checking 14 million on possibilities

So I did the unthinkable. I gave up by tweeting my frustration about the integration:

Then a Hero landed heroically into the thread:

Daniel Roe

Daniel Roe is well-known architect that works on the Nuxt Framework and an OSS saviour.

So we jumped on Discord, I created the reproduction repo and send him the logs and explained the context. (Here I want to make a special mention of the fact that Dani was on holiday, and his dedication to helping is so big, that he took the time to help 1 user, me, to solve my issue, I was amazed 😍)

After a few messages:

Daniel Croe Solution

Issue is your dist symlink. Just remove it.
It was set to an absolute path rather than a relative one
Enter fullscreen mode Exit fullscreen mode

🤔 wait what.... so maybe if I change the publish directory for ./dist it would work? Worth trying.

Netlify Build success

😱 IT WORKED!!!! IT FINALLY WORKED!!!!

But why? Why does the publish needs to be ./dist and not .output/public

The answer is right here in the code of Nitro is the server-engine of Nuxt.

It's because the Netlify Preset sets it to dist by default rather than .output (Because that's the netlify default location).

That's intended to support zero-config. So I asked if it makes sense to add zero-config preset for deploying between a monorepo. There is already an issue opened that could make it possible.

`workspaceDir` #158

pi0 avatar
pi0 posted on

For the conditions that nuxt app is not in the root of workspace/repository directory, often issues happen. Currently, we have an internal modulesDir option that helps to resolve this by creating a list of directories looking for node_modules (and by default process.cwd() is added to this list) but it is neither explicit or solving all cases.

Related issues:

Vite tries to find root by looking up for pnpm-workspace.yaml or a package.json with workspaces key but it is not enough since there are conditions user is not using a monorepo workspace but simply have nuxt app in a sub-directory. (https://vitejs.dev/config/#server-fsserve-root)

We can implement this by looking up (from rootDir) for first package.lock, package-lock.json or package.json that has workspaces key (unless there is a package.json in rootDir?) and falling back to rootDir if couldn't find. and also giving users ability to customize it with a nuxt option.

Final Form

Until a zero-config preset for monorepos is out there, you can use this final form of the netlify.toml to make it work 😜.

This is not even my final form

[build.environment]
  NODE_VERSION = "16"
  NPM_FLAGS = "--version" # prevent Netlify npm install
[build]
  base = "apps/blog/"
  publish = "./dist"
  command = "npx pnpm i --store=node_modules/.pnpm-store --frozen-lockfile && npx pnpm run generate:ci"
[[redirects]]
  from = "/*"
  to = "index.html"
  status = 200
Enter fullscreen mode Exit fullscreen mode

 Wrap up

This long experience helped me to understand a lot of things, like how pnpm monorepos work, how Nuxt is deployed on Netlify and most important:

The immense human quality and dedication of the contributors of OSS (In this case Daniel Roe from Nuxt and Zoltan Kochan of pnpmjs)

Please consider Sponsor their work in the links above.

You can find the code for this repo here https://github.com/alvarosabu/pnpm-monorepo-nuxt

It's all for today folks. Happy coding!!!

Discussion (1)

Collapse
kissu profile image
Konstantin BIFERT

Haha, you reached the 2 most dedicated people when asking for some help. 👌🏻
Of course your issue wouldn't last more than 1hour, calling the real heroes! 💪🏻

Great that the setup now works! 🌟