module.exports изначально содержит пустой объект, exports - ссылка на этот же объект
Вы можете расширять этот объект свойствами, либо присвоить в module.exports что-то другое, только надо учитывать что exports в этом случае будет по-прежнему ссылаться на изначальный объект
После того как модуль отработал во вне вернется то что в самом конце оказалось в module.exports
Примерно все работает так:
Весь код модуля оборачивается в такую конструкцию:
(function(module, require, exports) {
// код модуля вставляется сюда
});
Когда Вы первый раз реквайрите модуль срабатывает примерно следующий код:
var module = new Module(pathToModuleFile);
var moduleFunction = loadCodeAndRunInVM(module.id);
moduleFunction(module, module.require, module.exports); //Выполнили модуль
cache[module.id] = module.exports;
return module.exports;
При повторном вызове просто отдается значение из кэша