netlify / netlify/primitives

@netlify/dev: site base directory resolved against projectRoot, breaking dev when the site lives in a subdirectory

Open
#733 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
26
Forks
21
Avg merge
22h 38m
Merged PRs (30d)
13

Description

Summary

When a site lives in a subdirectory of the repository (e.g. www/) and the checkout is linked to a Netlify site whose Base directory is set to that same subdirectory, NetlifyDev.start() throws and the dev server exits before becoming ready.

The base directory ends up applied twice: <repo>/www + www<repo>/www/www.

When resolving config file <repo>/www/netlify.toml:
Base directory does not exist: <repo>/www/www

Cause

@netlify/vite-plugin constructs NetlifyDev with the Vite project root:

const netlifyDev = new NetlifyDev({
  ...,
  projectRoot: viteDevServer.config.root,
})

@netlify/dev then passes that same value through as repositoryRoot:

async getConfig() {
  const configFilePath = path.resolve(this.#projectRoot, 'netlify.toml')
  const config = await resolveConfig({
    config: configFileExists ? configFilePath : undefined,
    offline: !this.#siteID,
    repositoryRoot: this.#projectRoot,   // <-- Vite project root, not the repository root
    siteId: this.#siteID,
  })
}

Because a siteId is present, offline is false, so @netlify/config fetches the site and maps build_settings.base into defaultConfig.build.base (packages/config/lib/api/build_settings.js — "Netlify UI build settings are used as default configuration values"). getBase() then resolves that value against repositoryRoot, and getBuildDir() throws because the resulting directory does not exist.

Netlify documents the base directory as relative to the repository root. When the site lives in a subdirectory, projectRoot is already that base directory, so resolving the setting against it applies the same path segment a second time.

Reproduce

  1. A repo whose site lives in www/ — its own package.json and netlify.toml, and no base key in that toml.
  2. A Netlify site with Base directory set to www (correct, and required for the build to work).
  3. netlify link
  4. npm run dev (Astro + @astrojs/netlify)

The dev server exits printing only Dev server process exited before becoming ready. — the real error is in .astro/dev.log, since astro dev daemonizes.

Removing the siteId from www/.netlify/state.json makes it work again, because offline: !this.#siteID becomes true and no build settings are fetched. That is what makes this look intermittent: it is latent until the checkout is linked.

Expected

Local dev should resolve the base directory against the repository root — or ignore it when projectRoot already is the base directory — so that a configuration which builds correctly on Netlify also runs locally.

Workaround

Set NETLIFY_DEV=true so @netlify/vite-plugin returns no plugins and nothing resolves the config:

function netlify(options = {}) {
  if (process.env.NETLIFY_DEV) return []

This costs the local emulation (image CDN proxy, edge function sandbox, environment variable injection).

Notes

Deploys are unaffected: getSiteInfo short-circuits on mode === 'buildbot', since the buildbot passes the settings itself. This is local emulation only.

Versions

package version
@netlify/dev 4.18.11
@netlify/vite-plugin 2.12.9
@netlify/config 25.1.1
@astrojs/netlify 8.1.3
astro 7.1.6
node 24.18.0

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the NetlifyDev.getConfig() path in @netlify/dev and the Netlify Vite plugin construction of NetlifyDev, then inspect packages/config/lib/api/build_settings.js. Reproduce with a linked site whose base directory is www and a projectRoot of www. Done means local dev starts for this layout without resolving www twice while retaining local emulation.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js, typescript, vite
Domain
build-system, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.