Skip to content

doc: ES module dummy loader resolve hook bug on WindowsΒ #29610

@DerekNonGeneric

Description

@DerekNonGeneric
  • Version: 12.10.0
  • Platform: Windows Server 2019
  • Subsystem: url

An error occurs after creating the custom-loader.mjs containing the dummy loader code as directed by the documentation, an x.js file, and running the following command in PowerShell.

node --experimental-modules --loader ./custom-loader.mjs x.js

By the way, this command diverges from the one provided in the documentation to be used on Windows as NODE_OPTIONS='--experimental-modules --loader ./custom-loader.mjs' node x.js fails in PowerShell.

(node:1364) ExperimentalWarning: The ESM module loader is experimental.
(node:1364) ExperimentalWarning: --loader is an experimental feature. This feature could change at any time
internal/modules/cjs/loader.js:992
      internalBinding('errors').triggerUncaughtException(
                                ^

TypeError [ERR_INVALID_URL]: Invalid URL: /C:/Users/Administrator/source/repos/esm/x.js
    at onParseError (internal/url.js:243:9)
    at new URL (internal/url.js:319:5)
    at resolve (file:///C:/Users/Administrator/source/repos/esm/custom-loader.mjs:23:20)
    at Loader.resolve (internal/modules/esm/loader.js:73:33)
    at Loader.getModuleJob (internal/modules/esm/loader.js:152:40)
    at Loader.import (internal/modules/esm/loader.js:136:28)
    at internal/modules/cjs/loader.js:989:27 {
  input: '/C:/Users/Administrator/source/repos/esm/x.js'
}

The x.js file certainly exists. It turns out that there is a Windows-specific error where a leading forward slash causes this example to break. I was able to resolve this by adding:

  specifier = cleanPath(specifier);
  specifier = url.pathToFileURL(specifier).href;

prior to (from the dummy loader code):

 const resolved = new URL(specifier, parentModuleURL);

and including the following function:

/**
 * Path sanitizer that removes a leading slash if followed by Windows drive sepcifier.
 * @param {string} specifier URL path to a file
 * @returns {string} Cleaned specifier URL path to a file
 */
function cleanPath(specifier) {
  const specifierDir = path.parse(specifier).dir;

  if (
    specifierDir.length >= 3 &&
    specifierDir.charAt(0) == '/' &&
    specifierDir.charAt(1).toUpperCase() !=
      specifierDir.charAt(1).toLowerCase() && // Check if alphabetic
    specifierDir.charAt(2) == ':'
  ) {
    specifier = specifier.substring(1);
  }

  return specifier;
}

P.S. This also needs import url from 'url'; added to the top of custom-loader.mjs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions