skip to Main Content

I’m playing with NodeJS loaders where I want to intercept all calls to require and modify the source (transpile). When using ESM modules with import it works, but with CommonJS it doesn’t trigger the loader when calling require.

Here is the loader code:

async function load(url, context, nextLoad) {
  console.log({ url, context })
  return nextLoad(url)
}

async function resolve(specifier, context, nextResolve) {
  console.log({ specifier, context })
  return nextResolve(specifier)
}

module.exports = { load, resolve }

I’m injecting the loader by running node --loader=./loader.js hello.js from terminal.

And here is the CommonJS code for hello.js:

var lib = require('./lib.js')

The require('./lib.js') line neither triggers the loader resolve nor the load functions in the loader.

If I rewrite using ESM .mjs and import it works as expected.

Any ideas?

2

Answers


  1. Chosen as BEST ANSWER

    When using custom loaders with CommonJS, the files you require cannot only contain async functions, or the main thread might kill the hook thread before fully loading the file. This is from the documentation:

    Hooks are run in a separate thread, isolated from the main thread where application code runs. That means it is a different realm. The hooks thread may be terminated by the main thread at any time, so do not depend on asynchronous operations (like console.log) to complete.

    This isn't reached when required from the main thread:

    console.log('hello')
    

    but this works:

    setTimeout(function() {
      console.log('hello')
    }, 100)
    

    or any other synchronous code. In long running applications, like a web server application, it will also work without issues.


  2. Loaders in Node.js for ECMAScript Modules (ESM) and CommonJS modules. The loader you have defined works with ESM imports, but it doesn’t automatically handle CommonJS require calls because they use a different mechanism for module resolution and loading.

    If I didn’t wan’t to use a build tool, I would use Node.js’s require hook mechanism; but it’s more complex and less flexible IMHO.

    const Module = require('module');
    const fs = require('fs');
    
    Module._extensions['.js'] = function (module, filename) {
      const content = fs.readFileSync(filename, 'utf8');
      const transformedContent = transformContent(content); 
      module._compile(transformedContent, filename);
    };
    
    function transformContent(content) {
      // Implement your transpiling logic here
      return content;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search