import | import notebook | Cell 2 | Search

The code imports Node.js modules and defines two functions: getCached, which returns a cached module if it exists and is up-to-date, and makeModule, which creates and caches a new module object.

Run example

npm run import -- "how does node module require work"

how does node module require work

const Module = require('module').Module
const path = require('path')
const fs = require('fs')

function getCached(filepath) {
    // must have a new name for every generation otherwise cache will be returned
    var mtime = fs.statSync(filepath
        // TODO: find a better way to serve notebook names
        .replace(/\.ipynb(\[[0-9]+\]).*$/ig, '.ipynb')).mtime.getTime();

    // TODO: don't use cache of parent modules that have includes that have changed
    var cachedModule = Module._cache[filepath];
    if (cachedModule && mtime < cachedModule.buildTime) {
        return cachedModule;
    }
}

function makeModule(code, pathToCode, ctxGlobal) {
    const {CONSOLE} = require('../Core')
    var filepath = path.resolve(process.cwd(), pathToCode);

    ctxGlobal = ctxGlobal || {}
    ctxGlobal.module = getCached(filepath)
    if (ctxGlobal.module) {
        if (!ctxGlobal.module.loaded) {
            return ((module) => new Promise(resolve => {
                let inter = setInterval(() => {
                    if (module.loaded) {
                        clearInterval(inter)
                        resolve()
                    }
                }, 100)
            }).then(() => module.exports))(ctxGlobal.module)
        }
        return ctxGlobal.module.exports
    }
    delete require.cache[filepath]
    Object.assign(ctxGlobal, {
        importer: require('../Core'),
        module: new Module(filepath, module),
        exports: { original: true },
        __dirname: path.dirname(pathToCode),
        __filename: pathToCode.split('[')[0],
        $: void 0,
        console: CONSOLE,
        global: ctxGlobal,
        //require: require,
    })
    Object.assign(ctxGlobal.module, {
        module: ctxGlobal.module,
        exports: ctxGlobal.exports,
        parent: module,
        buildTime: (new Date()).getTime(),
        filename: pathToCode,
        paths: Module._nodeModulePaths(path.dirname(filepath))
    })
    Module._cache[filepath] = ctxGlobal.module;

    // this is basically the magic of the 'rewire' module, reuse this?
    const validVars = Object.keys(ctxGlobal)
        .filter(k => k.match(/^(\$\$|[a-z_][a-z0-9_]*)$/i))
        .join(',');

    if (!pathToCode.includes('Core')) {
        CONSOLE.info(`compiling ${pathToCode} with ${validVars}`);
    }

    // TODO: use the transpiler or kernels here
    try {
        ctxGlobal.module._compile(`
        module.exports = (({${validVars}}) => {
        ${code}
        return module.exports || exports || {}
        })`, filepath);

        // assign the actual imports internally by calling the func export
        var wrapperContext = ctxGlobal.module.exports;
        var result = wrapperContext(ctxGlobal);
        ctxGlobal.module.loaded = true;
        ctxGlobal.module.exports = result;
        return result;
    } catch (err) {
        if (err.code === 'ERR_REQUIRE_ESM') {
            const { makeESModule } = require('../Core')
            CONSOLE.error('ES module required', err)
            return makeESModule(code, filepath, ctxGlobal)
        } else {
            throw err
        }
    }
}

module.exports.makeModule = makeModule;

What the code could have been:

const { Module } = require('module');
const path = require('path');
const fs = require('fs');
const CONSOLE = require('../Core').CONSOLE;

class ModuleGenerator {
  constructor() {
    this.cache = {};
    this.cacheTimers = {};
  }

  async getCached(filepath) {
    const filename = path.basename(filepath);
    const mtime = fs.statSync(filepath).mtime.getTime();
    const cachedModule = this.cache[filepath];
    if (cachedModule && mtime < cachedModule.buildTime) {
      return cachedModule;
    }
    return null;
  }

  async makeModule(code, pathToCode, ctxGlobal) {
    try {
      ctxGlobal = ctxGlobal || {};
      ctxGlobal.module = await this.getCached(pathToCode);

      if (ctxGlobal.module) {
        if (!ctxGlobal.module.loaded) {
          await this.waitForModuleLoad(ctxGlobal.module);
        }
        return ctxGlobal.module.exports;
      }

      delete require.cache[pathToCode];
      const module = new Module(pathToCode, module);
      Object.assign(ctxGlobal, {
        importer: require('../Core'),
        module,
        exports: { original: true },
        __dirname: path.dirname(pathToCode),
        __filename: path.basename(pathToCode),
        $: void 0,
        console: CONSOLE,
        global: ctxGlobal,
      });
      Object.assign(module, {
        module,
        exports: ctxGlobal.exports,
        parent: module,
        buildTime: Date.now(),
        filename: pathToCode,
        paths: Module._nodeModulePaths(path.dirname(pathToCode)),
      });
      this.cache[pathToCode] = module;

      const validVars = Object.keys(ctxGlobal)
       .filter((k) => k.match(/^(\$\$|[a-z_][a-z0-9_]*)$/i))
       .join(',');

      if (!pathToCode.includes('Core')) {
        CONSOLE.info(`compiling ${pathToCode} with ${validVars}`);
      }

      module._compile(`
        module.exports = (({${validVars}}) => {
          ${code}
          return module.exports || exports || {}
        })`, pathToCode);

      const wrapperContext = module.exports;
      const result = await wrapperContext(ctxGlobal);
      module.loaded = true;
      module.exports = result;
      return result;
    } catch (err) {
      if (err.code === 'ERR_REQUIRE_ESM') {
        const { makeESModule } = require('../Core');
        CONSOLE.error('ES module required', err);
        return makeESModule(code, pathToCode, ctxGlobal);
      } else {
        throw err;
      }
    }
  }

  async waitForModuleLoad(module) {
    return new Promise((resolve) => {
      const interval = setInterval(() => {
        if (module.loaded) {
          clearInterval(interval);
          resolve();
        }
      }, 100);
    });
  }
}

module.exports.makeModule = async (code, pathToCode, ctxGlobal) => {
  const generator = new ModuleGenerator();
  return generator.makeModule(code, pathToCode, ctxGlobal);
};

Breakdown of the Code

Importing Dependencies

The code starts by importing the following modules:

const Module = require('module').Module
const path = require('path')
const fs = require('fs')

getCached Function

The getCached function takes a filepath as an argument and returns a cached module if it exists and is up-to-date:

function getCached(filepath) {
  //...
}

The function checks if the file at filepath has changed by comparing its modification time with the cached module's build time. If the cached module is up-to-date, it is returned.

makeModule Function

The makeModule function takes three arguments:

The function returns a module object that can be executed:

function makeModule(code, pathToCode, ctxGlobal) {
  //...
}

Here's a high-level overview of what the function does:

  1. It resolves the filepath using path.resolve.
  2. It checks if a cached module exists for the filepath. If it does, it returns the cached module or its exports if the module is already loaded.
  3. If no cached module exists, it creates a new module object using the Module class from Node.js.
  4. It assigns properties to the module object, such as imports, exports, and filename.
  5. It adds the module object to the cache.
  6. It returns the module object.

Note that the function uses Object.assign to copy properties from one object to another, which can be done in a more modern way using object destructuring and the spread operator ({...obj}).

Miscellaneous

The code also uses some Node.js-specific features, such as:

Overall, the code appears to be implementing a custom module loading mechanism that uses caching to improve performance.