Когда вы вызываете require('module1') нода вычисляет полный путь к модулю и проверяет есть ли в кэше соответствующая запись? Если есть, то возвращается объект exports из этого кэша. Если нет, то создаётся новая запись в кэше и вызывается IIFE function(module) { /* тут ваш файл */ }(cache['/path/to/module1']). Заметим, что поле exports по умолчанию пустой объект. Если вы в своём модуле сразу вызвали require('module2') который в свою очередь сразу вызвал module1, то модулю2 вернётся объект exports из кэша, т.е. пустой. Если же сначала заменить module.exports на то, что нам надо, то он сразу поменяется и в кэше (помним, что объекты передаются по ссылке) и модуль 2 получит наш класс, а не пустой объект.
Ещё раз повторюсь, это очень упрощённое описание и относится только в CommonJS модулям. В ESM (которые import .. from) всё несколько по другому.
Права на запись есть?
Скрипт запускается в какой папке?
Стандартная ошибка, что относительный путь вычисляется от текущей папки в момент запуска.