Repository: nodejs/node
Pull request: esm: add a fall back when importer in not a file

There's this assumption in the code that the importing module (which I called the `importer` in the PR title) URL would be under the `file:`  scheme, but there's no reason this is the case, even without loader it could be under the `data:` scheme. TBH I'm not sure why we bother converting it to a path instead of exposing the URL and let the user decides if they want to convert it to a path, but for the sake of not introducing a breaking change, I'm simply wrapping the `fileURLToPath` in a `try`/`catch` block.
<!--
Before submitting a pull request, please read:

- the CONTRIBUTING guide at https://github.com/nodejs/node/blob/HEAD/CONTRIBUTING.md
- the commit message formatting guidelines at
  https://github.com/nodejs/node/blob/HEAD/doc/contributing/pull-requests.md#commit-message-guidelines

For code changes:
1. Include tests for any bug fixes or new features.
2. Update documentation if relevant.
3. Ensure that `make -j4 test` (UNIX), or `vcbuild test` (Windows) passes.

If you believe this PR should be highlighted in the Node.js CHANGELOG
please add the `notable-change` label.

Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.
-->


Changed files:
filename: lib/internal/modules/esm/resolve.js
patch: @@ -227,9 +227,15 @@ const encodedSepRegEx = /%2F|%5C/i;
  */
 function finalizeResolution(resolved, base, preserveSymlinks) {
   if (RegExpPrototypeExec(encodedSepRegEx, resolved.pathname) !== null) {
+    let basePath;
+    try {
+      basePath = fileURLToPath(base);
+    } catch {
+      basePath = base;
+    }
     throw new ERR_INVALID_MODULE_SPECIFIER(
       resolved.pathname, 'must not include encoded "/" or "\\" characters',
-      fileURLToPath(base));
+      basePath);
   }
 
   let path;
@@ -248,14 +254,26 @@ function finalizeResolution(resolved, base, preserveSymlinks) {
 
   // Check for stats.isDirectory()
   if (stats === 1) {
-    throw new ERR_UNSUPPORTED_DIR_IMPORT(path, fileURLToPath(base), String(resolved));
+    let basePath;
+    try {
+      basePath = fileURLToPath(base);
+    } catch {
+      basePath = base;
+    }
+    throw new ERR_UNSUPPORTED_DIR_IMPORT(path, basePath, String(resolved));
   } else if (stats !== 0) {
     // Check for !stats.isFile()
     if (process.env.WATCH_REPORT_DEPENDENCIES && process.send) {
       process.send({ 'watch:require': [path || resolved.pathname] });
     }
+    let basePath;
+    try {
+      basePath = fileURLToPath(base);
+    } catch {
+      basePath = base;
+    }
     throw new ERR_MODULE_NOT_FOUND(
-      path || resolved.pathname, base && fileURLToPath(base), resolved);
+      path || resolved.pathname, basePath, resolved);
   }
 
   if (!preserveSymlinks) {

filename: test/es-module/test-esm-main-lookup.mjs
patch: @@ -15,6 +15,14 @@ await assert.rejects(import('../fixtures/es-modules/pjson-main'), {
   code: 'ERR_UNSUPPORTED_DIR_IMPORT',
   url: fixtures.fileURL('es-modules/pjson-main').href,
 });
+await assert.rejects(import(`data:text/javascript,import${encodeURIComponent(JSON.stringify(fixtures.fileURL('es-modules/pjson-main')))}`), {
+  code: 'ERR_UNSUPPORTED_DIR_IMPORT',
+  url: fixtures.fileURL('es-modules/pjson-main').href,
+});
+await assert.rejects(import(`data:text/javascript,import${encodeURIComponent(JSON.stringify(fixtures.fileURL('es-modules/does-not-exist')))}`), {
+  code: 'ERR_MODULE_NOT_FOUND',
+  url: fixtures.fileURL('es-modules/does-not-exist').href,
+});
 
 assert.deepStrictEqual(
   { ...await import('../fixtures/es-modules/pjson-main/main.mjs') },