Проблемы с кешированием в ESM



Андрей Ситник анонсировал переход на нативные модули в одном из своих проектов, а месяц назад Sindre Sorhus сообщил, что собирается перевести больше 1000 своих проектов. Самое интересное в этой части треда: ESM сложно мокать.



В ишью Invalidate cache when using import ищут альтернативу удаления модулей из кеша. При использовании CJS это делается примерно так: delete require.cache[require.resolve('./b.js’)]. В случае ESM доступ к кешу отсутствует. Если в импорте добавить query параметры, то нода будет считать, что это разные модули. Это позволяет загружать модуль ещё раз (например при watch режиме в тестах).



Так же нода поддержвает лоадеры для модулей. Таким образом можно обходить кеш, но нужно помнить, что модули всё равно останутся в памяти и будут занимать мегабайты.



Вот ишью о поддержке ESM и watch-режима в mocha (правда интересного там ничего нет). А вот ишью в Jest, и тут всё интересней. А именно пункт jest.(do|un)mock.



Здесь надо чуть-чуть погрузиться в то, как это работает сейчас. jest.mock хоиститься (переносится в самый верх файла), таким образом можно сперва заимпортировать модуль, чуть ниже мокнуть его, а на деле после транспайлинга мы получим jest.mock на самом верху, а уже после require вместо import.



Нативные модули в свою очередь так же хоистятся (даже если импорты в самом низу файла), а это значит, что мы не можем ничего выполнить до импортов. Один из возможных воркэраундов трансформировать синхронные импорты в асинхронные. Т.е. import “foo” преобразовывать в import(“foo”). Таким образом можно будет выполнять код перед импортом, и дожидаться инициализации через top-level await. Ну а пока можно следить за реализацией jest.mockModule.



И бонусом: Антон Корзунов в треде упомянул статью «Mock all you want: supporting ES modules in the Testdouble.js mocking library». В статье как раз описывается воркэраунд с лоадером.