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.
npm run import -- "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;
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
The code starts by importing the following modules:
const Module = require('module').Module
const path = require('path')
const fs = require('fs')
Module is imported from the Node.js module module, and Module is accessed directly to use its methods.path is a built-in Node.js module for working with file paths.fs is a built-in Node.js module for interacting with the file system.getCached FunctionThe 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 FunctionThe makeModule function takes three arguments:
code: the code to executepathToCode: the path to the code filectxGlobal: the global context objectThe 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:
filepath using path.resolve.filepath. If it does, it returns the cached module or its exports if the module is already loaded.Module class from Node.js.imports, exports, and filename.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}).
The code also uses some Node.js-specific features, such as:
require.cache to clear the cache for a specific file.Module._cache to access the cache of loaded modules.Module._nodeModulePaths to get the paths of node modules.Overall, the code appears to be implementing a custom module loading mechanism that uses caching to improve performance.