loaders-test icon indicating copy to clipboard operation
loaders-test copied to clipboard

commonjs-extension-resolution-loader breaks packages using module and exports.* fields

Open wojtekmaj opened this issue 1 year ago • 1 comments

Because commonjs-extension-resolution-loader relies on resolve/async which is an implementation of require.resolve(), specifying it as a Node.js loader causes ESM packages to break.

For example,

index.js

import isValidAbn from 'is-valid-abn';

works fine with node --experimental-specifier-resolution=node index.js, but with node --experimental-loader=./commonjs-extension-resolution-loader index.js breaks with:

file:///…/index.js:2
import isValidAbn from 'is-valid-abn';
       ^^^^^^^^^^
SyntaxError: The requested module 'is-valid-abn' does not provide an export named 'default'
    at ModuleJob._instantiate (node:internal/modules/esm/module_job:123:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:189:5)
    at async Promise.all (index 0)
    at async ESMLoader.import (node:internal/modules/esm/loader:530:24)
    at async loadESM (node:internal/process/esm_loader:91:5)
    at async handleMainPromise (node:internal/modules/run_main:65:12)

What makes the error go away:

  • Monkey patching main field in is-valid-abn's package.json to point to ESM entry file
  • Monkey patching resolve package to include:
      if (pkg && pkg.module) {
          if (typeof pkg.module !== 'string') {
              var mainError = new TypeError('package “' + pkg.name + '” `main` must be a string');
              mainError.code = 'INVALID_PACKAGE_MAIN';
              return cb(mainError);
          }
          if (pkg.module === '.' || pkg.module === './') {
              pkg.module = 'index';
          }
          loadAsFile(path.resolve(x, pkg.module), pkg, function (err, m, pkg) {
              if (err) return cb(err);
              if (m) return cb(null, m, pkg);
              if (!pkg) return loadAsFile(path.join(x, 'index'), pkg, cb);
    
              var dir = path.resolve(x, pkg.module);
              loadAsDirectory(dir, pkg, function (err, n, pkg) {
                  if (err) return cb(err);
                  if (n) return cb(null, n, pkg);
                  loadAsFile(path.join(x, 'index'), pkg, cb);
              });
          });
          return;
      }
    

This, obviously, doesn't get the support for exports field back.

wojtekmaj avatar Dec 09 '22 12:12 wojtekmaj