Skip to content

resolvePath should strip trailing slashes from node: builtin specifiers #555

Description

@ashtonjurgens

Environment

I've seen this bug in both node and deno

node v25.8.1

deno 2.8.2 (stable, release, aarch64-apple-darwin)
v8 14.9.207.2-rusty
typescript 6.0.3

Reproduction

See description...

Describe the bug

Summary

resolvePath currently preserves trailing slashes when a specifier resolves to a node: builtin.

That is a problem because normal package imports may legally use a trailing slash, but node: builtin specifiers must not.

For example, Node accepts:

await import("punycode/")

but rejects:

await import("node:punycode/")

with:

Error [ERR_UNKNOWN_BUILTIN_MODULE]: No such built-in module: node:punycode/

Real-world case
This showed up with punycode, which exists both as an npm package and as a Node builtin.
A dependency imported punycode/, and unenv converted that to the builtin form. The trailing slash was preserved, so the final specifier became node:punycode/, which is invalid in Node.
Reproduction

const { env } = defineEnv({
  nodeCompat: false,
  resolve: true,
  overrides: {
    alias: {
      punycode: "node:punycode",
      foo: "punycode/",
    },
  },
});
env.alias.foo;

Current result:

"node:punycode/"

Expected result:

"node:punycode"

Expected behavior
When resolvePath resolves a specifier to a builtin module, it should normalize a trailing slash away before returning the node: form.
This normalization should only apply to builtins. Regular non-builtin imports like my-module/ should remain unchanged.

Additional context

No response

Logs

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions