I found this old post while researching this very question. I had figured out how to shoehorn in __line using an approach like what is in magic-globals (e.g. capturing the error stack, etc). The problem with that is that is SUPER slow if you are calling it frequently. I am using these macros a lot since I am maintaining my own, separate call stack for a transpiled language. The code was 25X slower when constantly capturing the native stack.
I don't know if this option was available in 2014, but as of 2024 you can inject your own loader logic. Example:
const fs = require('fs'), Module = require('module'); /** * * @param {string} ext The file extension to configure the loader for * @param {function(string, Module): boolean} checkExclude Callback to see if we should defer to the original loader * @returns */ function configureExtensionLoader(ext = '.js', checkExclude = false) { const originalRequire = Module._extensions[ext]; /** * * @param {string} filename The file being processed * @param {string} contentIn The content of the file */ function preprocess(filename, contentIn) { const ext = filename.slice(filename.lastIndexOf('.')), content = contentIn.split('\n').map((line, index) => { line = line.replace(/__line/g, index + 1); line = line.replace(/__ext/g, ext); return line; }).join('\n'); // TODO: Add support for __class, __methodName, etc return content; } /** * Load a module * @param {Module} module * @param {string} filename The module file to load */ Module._extensions[ext] = function (module, filename) { const excluded = checkExclude && checkExclude(filename, module); if (!excluded) { const content = fs.readFileSync(filename, 'utf8'); const preprocessedContent = preprocess(filename, content); module._compile(preprocessedContent || content, filename); } else return originalRequire(module, filename); }; return true; } /** * Configure the preprocessor for a list of extensions. * @param {string[]} extensionList The list of extensions to configure * @param {function(string, Module): boolean} checkExclude A callback that can exclude modules from preprocessing */ module.exports = function (extensionList = ['.js'], checkExclude) { for (ext of extensionList) { configureExtensionLoader(ext, checkExclude); } }
Usage example:
const path = require('node:path'), driverSource = path.join(__dirname, 'src'); // Nothing but the side-effects, mam require('./src/Importer')(['.js'], (filename) => !filename.startsWith(driverSource));
I include this in my entry script, and now any require() calls to include modules from my 'src' directory will automatically expand the __line macro to a numeric literal, and __ext will expand to '.js' literal. If I wanted to spend the time, I--or someone else--could write a simple tokenizer to provide __method, __class, __function, etc.