O víkendu jsem četl návrh import.meta pro JavaScript. Tento návrh má za cíl vyřešit např. problém přístupu k metainformacím modulu, například co je aktuální prvek skriptu.
// in Frontend land
// index.html
<script src="foo.js"></script>
// foo.js
const currentScript = document.currentScript
Takto byste to mohli udělat v prohlížeči, ale jak to funguje v Node.js? To mi přináší učení o víkendu. 🎉
Udělejme si nejprve rychlé osvěžení:v Node.js je každý modul a požadovaný soubor zabalen do tzv. modulového obalu.
(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});
Toto jsou require
funkční a komfortní objekty jako __filename
a __dirname
pocházejí z. V Node.js není currentScript
ale spíše máte jeden vstupní skript, který pak vyžaduje pravděpodobně tisíce dalších modulů. Jak byste nyní mohli zjistit, zda je skript vstupním skriptem?
Ukazuje se, že existují dva způsoby, jak toho dosáhnout. Existuje require
a process
. Pojďme se tedy podívat, co je definováno v těchto dvou.
// test.js
console.log(require.main);
console.log(process.mainModule);
// -------------------------------------
// output of `$ node test.js`
Module {
id: '.',
exports: {},
parent: null,
filename: '/private/tmp/foo.js',
loaded: false,
children: [],
paths:
[ '/private/tmp/node_modules',
'/private/node_modules',
'/node_modules' ] }
Module {
id: '.',
exports: {},
parent: null,
filename: '/private/tmp/foo.js',
loaded: false,
children: [],
paths:
[ '/private/tmp/node_modules',
'/private/node_modules',
'/node_modules' ] }
Dobře... takže cestu k souboru vstupního modulu můžete získat z require
nebo process
a tyto dva objekty také obsahují mnohem užitečnější informace.
Chcete-li zjistit, zda je modul vstupním skriptem, můžete jej zkontrolovat podle module
objekt.
const isEntryScript = require.main === module;
const isAlsoEntryScript = process.mainModule === module;
Ale jsou require
a process
vlastně to samé?
// test.js
console.log(require.main === process.mainModule);
// -------------------------------------
// output of `$ node test.js`
true
Huh, to je zajímavé – jsou... Tak jaký je v tom tedy rozdíl? Dokumenty jsou v tom poměrně nejasné.
Co to tedy znamená? Rozhodl jsem se trochu překopat základní kód Node.js.
process
je definován v node/lib/modules.js:
Module._load = function(request, parent, isMain) {
// ...
if (isMain) {
process.mainModule = module;
module.id = '.';
}
Module._cache[filename] = module;
tryModuleLoad(module, filename);
return module.exports;
};
require
je definován v node/lib/internals/modules
:
function makeRequireFunction(mod) {
// ...
require.main = process.mainModule;
// ...
return require;
}
Nekopal jsem dále do vnitřních částí, ale do require
funkce, kterou všichni používáme každý den, obsahuje skutečný odkaz na process
. To je důvod, proč jsou skutečně stejné. Co se stane, když změníme process
nebo require
?
// test.js
const bar = require('./foo');
console.log(process.mainModule);
console.log(require.main);
// foo.js
// changing both values
process.mainModule = 'schnitzel';
require.main = 'pommes';
// -------------------------------------
// output of `$ node test.js`
schnitzel
Module {
id: '.',
exports: {},
parent: null,
filename: '/private/tmp/foo.js',
loaded: false,
children:
[ Module {
id: '/private/tmp/bar.js',
exports: {},
parent: [Circular],
filename: '/private/tmp/bar.js',
loaded: true,
children: [],
paths: [Array] } ],
paths:
[ '/private/tmp/node_modules',
'/private/node_modules',
'/node_modules' ] }
Aha! Ukazuje se, že pokud nastavíme process
na něco jiného během běhu (nemám ponětí, proč bych to dělal, ale ano ¯_(ツ)_/¯) require
pak stále obsahuje odkaz na počáteční hlavní modul.
Upraveno:
Alexandre Morgaut poukázal na to, že require
je součástí specifikace CommonJS, a proto je součástí jádra Node.js.