Skip to content

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

Closed
@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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions