nitrojs / nitrojs/nitro

Nitro 2: node-externals constructs a trailing-slash package specifier, triggering DEP0155 for @vue/shared

Open Beginner friendly
#4,611 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

pending triage
Dominant language
TypeScript
Stars
11.2k
Forks
899
Avg merge
2d 24m
Merged PRs (30d)
40

Description

Environment
  • OS: Windows
  • Node.js: 24.19.0
  • Package manager: pnpm 12.3.4
  • Nuxt: 4.5.2
  • Nitro / nitropack: 2.13.4
  • Vue / @vue/shared: 3.5.42
  • mlly: 1.8.2
  • exsolve: 1.1.1
  • Build preset: node-server
  • Compatibility date: 2025-12-21

Relevant configuration:

export default defineNuxtConfig({
  compatibilityDate: '2025-12-21',
  nitro: {
    experimental: {
      websocket: true,
    },
  },
})

### Reproduction

he warning occurs during the Nitro server build phase of an existing Nuxt application.

To capture the stack trace, run the build with deprecation tracing enabled. In PowerShell:

```powershell
$env:NODE_OPTIONS = '--trace-deprecation'
pnpm build

I do not currently have a public minimal reproduction repository. However, I traced the warning in the actual build and independently reproduced the dependency-resolution sequence using the installed dependencies.

The isolated sequence is:

// vueFile is the resolved absolute path to:
// @vue/shared/dist/shared.esm-bundler.js

const subpath = await lookupNodeModuleSubpath(vueFile)
// "./"

const specifier = join('@vue/shared', subpath)
// "@vue/shared/"

resolveModuleURL(specifier, {
  from: importer,
  try: true,
  conditions: ['node', 'import', 'production'],
})
// Emits DEP0155 and returns undefined.

Here:

  • lookupNodeModuleSubpath is from mlly.
  • join is from pathe, as used by Nitro.
  • resolveModuleURL is from exsolve.
  • importer is the file URL of Nuxt's installed error-500.mjs template.

As a control, resolving @vue/shared with the same resolver options succeeds without the warning.

Describe the bug

During the Nitro server build, the node-externals plugin constructs the package specifier @vue/shared/ while trying to infer an importable package subpath. Passing this specifier to exsolve triggers a DEP0155 deprecation warning.

The original import in @nuxt/nitro-server/dist/runtime/templates/error-500.mjs does not contain a trailing slash:

import { escapeHtml } from '@vue/shared'

The observed resolution sequence is:

  1. mlly.lookupNodeModuleSubpath() returns "./" for the package root export.
  2. Nitro joins the package name with this subpath.
  3. join('@vue/shared', './') produces @vue/shared/.
  4. Nitro passes that specifier to exsolve.resolveModuleURL().
  5. exsolve emits DEP0155 while resolving the resulting "./" package subpath.

The actual build stack points to this statement in nitropack/dist/rollup/index.mjs:

const resolvedGuess =
  guessedSubpath && tryResolve(join(pkgName, guessedSubpath), importer)

Expected behavior

When the inferred subpath represents the package root, Nitro should resolve the bare package name, such as @vue/shared, without introducing a trailing slash.

Actual behavior

Nitro attempts to resolve @vue/shared/, producing the deprecation warning.

Additional context

The installed @vue/shared/package.json contains export keys "." and "./*". It does not contain a separate "./" export key.

Directly importing the Nuxt error-page template with Node.js and --trace-deprecation succeeds without emitting this warning.

Although mlly returns the "./" subpath, the warning in the actual build is emitted by exsolve, called from Nitro's node-externals plugin.

Logs
Local absolute paths have been shortened for readability.


[DEP0155] DeprecationWarning: Use of deprecated trailing slash pattern mapping "./"
in the "exports" field module resolution of the package at
<project>/node_modules/.pnpm/@nuxt+nitro-server@4.5.2_<hash>/node_modules/@vue/shared/package.json
imported from
<project>/node_modules/.pnpm/@nuxt+nitro-server@4.5.2_<hash>/node_modules/@nuxt/nitro-server/dist/runtime/templates/error-500.mjs.
Mapping specifiers ending in "/" is no longer supported.

    at emitTrailingSlashPatternDeprecation (exsolve/dist/index.mjs:650:12)
    at packageExportsResolve (exsolve/dist/index.mjs:669:38)
    at packageResolve (exsolve/dist/index.mjs:771:82)
    at moduleResolve (exsolve/dist/index.mjs:809:14)
    at _tryModuleResolve (exsolve/dist/index.mjs:915:10)
    at resolveModuleURL (exsolve/dist/index.mjs:866:16)
    at tryResolve (nitropack/dist/rollup/index.mjs:346:17)
    at Object.resolveId (nitropack/dist/rollup/index.mjs:417:51)
    at async PluginDriver.hookFirstAndGetPlugin (rollup/dist/es/shared/node-entry.js:22743:28)
    at async resolveId (rollup/dist/es/shared/node-entry.js:21205:26)


Isolated reproduction output:


subpath: "./"
specifier: "@vue/shared/"

Resolving: @vue/shared
file:///<project>/node_modules/.pnpm/@vue+shared@3.5.42/node_modules/@vue/shared/dist/shared.cjs.prod.js

Resolving: @vue/shared/
undefined

[DEP0155] DeprecationWarning: Use of deprecated trailing slash pattern mapping "./" ...

Contributor guide

Open the contributing guide

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 at the node-externals path in nitropack/dist/rollup/index.mjs, especially the tryResolve call shown in the report, and compare it with the isolated mlly, pathe, and exsolve resolution sequence. Run the provided PowerShell reproduction with NODE_OPTIONS='--trace-deprecation'; done means the Nitro build resolves the package root without constructing a trailing-slash specifier or emitting DEP0155.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js, typescript
Domain
build-system
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.