Nitro 2: node-externals constructs a trailing-slash package specifier, triggering DEP0155 for @vue/shared
Nobody has claimed this yet.
- 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:
lookupNodeModuleSubpathis frommlly.joinis frompathe, as used by Nitro.resolveModuleURLis fromexsolve.importeris the file URL of Nuxt's installederror-500.mjstemplate.
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:
mlly.lookupNodeModuleSubpath()returns"./"for the package root export.- Nitro joins the package name with this subpath.
join('@vue/shared', './')produces@vue/shared/.- Nitro passes that specifier to
exsolve.resolveModuleURL(). exsolveemitsDEP0155while 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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