Skip to content

Since #847, a relative import with # loads the file named with the # instead of treating #… as a fragment #856

Closed

Description

@fengmk2

In an ES module import, # starts a URL fragment: Node.js resolves import './a#b.ts' as ./a with the fragment #b.ts. Since the fix for #837 (#847), oxc-node loads a file named a#b.ts instead, when one exists.

$ ls
a#b.ts  a.ts  main.ts  package.json
$ cat main.ts a.ts 'a#b.ts'
import './a#b.ts';
console.log('loaded a.ts');
console.log('loaded a#b.ts');

$ node main.ts                                     # Node.js type stripping
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/…/a' imported from /…/main.ts
$ tsx main.ts
loaded a.ts
$ node --import @oxc-node/core/register main.ts   # main at ed51b8b
loaded a#b.ts

With v0.1.7 the import failed as it does in Node.js, because the unencoded URL let Node.js drop the fragment. A genuine fragment still works: import './dep.ts#x' loads dep.ts. Reproduced on macOS arm64 with Node.js 24.12, through an embedder that compiles in main at ed51b8b, since that commit is not on npm.

Cause

oxc_resolver follows enhanced-resolve here: for a specifier with a # and no ?, load_parse (oxc_resolver 11.24.3, src/lib.rs) first tries the whole specifier as a path, and only then treats #… as a fragment. Now that #847 encodes the resolved path, Node.js loads that file.

Suggested fix

For ES module specifiers, split off the fragment by URL rules before resolving, so that # always starts a fragment as in Node.js, and append it to the resolved URL. CommonJS require() keeps treating # as part of the path, as Node.js does. File names with # given on the command line already arrive percent-encoded (%23) and keep working.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions