Je čas přejít na ESM-only. Ekosystém je připravený
V únoru 2025 vyzval Anthony Fu, autor populárních nástrojů kolem Vue, Nuxtu a Vite, ekosystém k opuštění duálního publikování npm balíčků a přechodu na ESM-only. S odstupem více než roku je jasné, že měl pravdu – a že se ekosystém posunul ještě rychleji, než sám čekal. Node.js dnes umí require() i na ESM moduly, podíl balíčků s podporou ESM přesáhl třetinu a komunita označuje rok 2026 za „rok plné adopce ESM“.
Nálepky:
Na začátku roku 2025 publikoval Anthony Fu (autor řady populárních nástrojů kolem Vue, Nuxtu a Vite) příspěvek, ve které mění názor. Předtím roky argumentoval pro duální publikování balíčků v CJS i ESM, aby se uživatelům usnadnila migrace. V únoru 2025 začal tvrdit, že je čas tuhle berličku odhodit. S odstupem více než roku se ukazuje, že měl pravdu, a že se ekosystém za ten rok posunul ještě rychleji, než kdokoliv čekal.
Kde jsme dnes
ECMAScript Modules jsou s námi od roku 2015, takže letos mají jedenáct let. Node.js je podporuje nativně od verze 12, prohlížeče dávno taky. A přesto většina npm balíčků stále vychází v CommonJS, případně v duální variantě, kdy se do publikovaného balíčku nacpou obě podoby kódu najednou.
Data ze skriptu npm-esm-vs-cjs od Titus Wormse, který sleduje populární balíčky na npm, ukazují jasný trend. Pokud počítáme balíčky s podporou ESM (tedy čisté ESM plus duální formát), bylo jich v roce 2021 pouze 7,8 %, ke konci roku 2024 už 25,8 % a v posledním crawlu z prosince 2025 je to 33,4 %. Rozložení v prosinci 2025 vypadalo takto: 55,9 % CJS, 20,8 % duální formát, 12,6 % čisté ESM a 10,7 % tzv. faux ESM (starší bundlerové konvence). CJS stále převažuje, ale pozornost si zaslouží detail – duální balíčky rostou rychleji než čisté ESM. A právě proti duálnímu přístupu Fu argumentuje: neřeší problém, jen ho zdvojuje.
Nástroje jsou připravené
První a nejpodstatnější věc: moderní nástroje ESM dávno nejenže umějí, ale berou ho jako výchozí. Vite, na kterém dnes stojí Nuxt, SvelteKit, Astro, SolidStart, Remix, Storybook a řada dalších meta-frameworků, byl od začátku postaven kolem ESM. Testovací framework Vitest byl designován jako ESM-first a z toho těží bohatým mockováním modulů a jemně zrnitým cachováním.
Pro běh TypeScriptu a ESM v Node.js bez složité konfigurace máme tsx a jiti. ESLint v deváté verzi zavedl nový flat config, který nativně podporuje ESM konfiguraci i v CJS projektech pomocí eslint.config.mjs.
Zkrátka: argument „ekosystém na to ještě není připraven“, který byl v roce 2021 legitimní, dnes už neobstojí.
Top-down versus bottom-up
Když Sindre Sorhus v roce 2021 začal agresivně migrovat své balíčky (find-up, execa a desítky dalších) na ESM-only, byl to bottom-up přístup – tlak zdola, od nízkoúrovňových utilit. Výsledkem bylo, že závisející projekty, které ještě ESM neuměly, zůstaly zamrzlé na starších verzích, a ekosystém se na chvíli fragmentoval.
Fu přiznává, že tehdy s tímhle postupem nesouhlasil, ale dnes je za něj vděčný. Získali jsme díky němu spoustu kvalitních ESM balíčků. Přesto argumentuje, že top-down přístup je v praxi schůdnější: když ESM přijmou vysoce postavené frameworky a vývojářské nástroje, zbytek ekosystému to přirozeně stáhne s sebou.
ESM balíček totiž může bez problémů záviset na CJS balíčcích, obráceně to fungovalo historicky špatně. A přesně tenhle problém teď Node.js řeší.
Node.js teď umí require() i na ESM moduly
Největší novinkou, kterou Fu ve svém článku označil za „neuvěřitelný milník“, je funkce, na které pracovala Joyee Cheung: možnost volat require() na ESM modul přímo. V době psaní originálu byla čerstvě odznačena a teprve backportovaná do LTS. Dnes je situace ještě lepší, než Fu popisoval. require(esm) byl na konci roku 2025 označen jako stabilní a je dostupný bez flagů ve všech podporovaných LTS verzích – v Node.js 20.19+ i v 22.12+. Prakticky každý, kdo běží na aktuálním Node.js, tuhle schopnost už má.
Co to znamená v praxi? Že balíček můžete publikovat jako ESM-only a CJS konzumenti ho stále mohou použít s minimálními úpravami. Vyhnete se tím nákaze asynchronicitou (známý problém, kterému se říká „red functions“), kterou způsobuje dynamický import() , a která se v některých případech migruje opravdu bolestně, ne-li vůbec.
Node.js k tomu zavedl i novou syntaxi export { Foo as 'module.exports' }, která v ESM umožňuje exportovat hodnotu kompatibilní s CommonJS. Autoři balíčků tak můžou publikovat ESM-only a přitom neporušit API pro CJS uživatele – jediná breaking change je obvykle zvýšení minimální verze Node.js.
Fu pro tohle má hezkou metaforu: místo top-down nebo bottom-up máme teď možnost migrovat middle-out, protože import chain ESM → CJS → ESM → CJS konečně funguje.
Proč je dvoj-publikování otrava
Na papíře zní duální publikování (CJS + ESM v jednom balíčku) jako elegantní kompromis. Ve skutečnosti si autoři balíčků i uživatelé kupují řadu problémů, které by při jednom formátu neměli.
Interop mezi formáty. CJS má jeden module.exports, ESM má default export a pojmenované exporty. Když autor píše v ESM a transpiluje do CJS, handling exportů se komplikuje, obzvlášť když je exportovaná hodnota funkce nebo třída, ne objekt. K tomu se přidávají .d.mts a .d.cts deklarační soubory, aby typy fungovaly v obou světech. Fu to shrnuje celkem výstižně: tímhle problémem by se uživatel balíčků vůbec neměl zabývat, a raději ani autor.
Rozlišení závislostí. Pokud duální balíček závisí na balíčku, který je ESM-only, musí se zařídit, aby se použila správná varianta. V projektech se stovkami tranzitivních závislostí to vede k verzovým konfliktům a u balíčků navržených jako singleton k riziku duplicitních instancí v paměti, což způsobuje tiché chyby.
Velikost balíčku. Dva bundly místo jednoho znamenají dvojnásobnou velikost. Pár kilobajtů se zdá nic, ale v projektu se stovkami závislostí se to sečte do pověstného nabobtnalého node_modules.
Kdy konkrétně přejít
Fu netvrdí, že všechno máme přes noc překlopit. Dává několik pragmatických kritérií:
Nové balíčky by měly být ESM-only vždy. Žádné legacy závislosti neřešíte, noví adoptéři jsou pravděpodobně na moderním stacku, úspora v údržbě je znatelná.
Balíčky pro prohlížeč mají ESM-only jako jasnou volbu. Bundlery z něj umějí static analysis a tree-shaking lépe než z CJS, výsledkem jsou menší bundly.
Standalone CLI nástroje. Koncovému uživateli je úplně jedno, jestli je vaše CLI v ESM nebo CJS. Ale použití ESM umožní ESM závislostem zapadnout do ekosystému – to je onen top-down tlak.
Starší balíčky s velkou uživatelskou základnou vyžadují rozmyslnější přístup. Fu radí: poznejte své konzumenty. Pokud je váš ESLint plugin určený pro ESLint v9, který ESM v konfiguraci nativně umí, nemáte důvod zůstávat na CJS.
Co to znamená pro českého vývojáře
Pro někoho, kdo dělá na moderním frontendu s Vite, Nuxtem nebo SvelteKitem, je ESM-only stav, ve kterém už dávno je, jen si to možná ani neuvědomuje. Problém se týká hlavně autorů knihoven a lidí, kteří udržují starší Node.js backendy.
Pokud maintainujete veřejný balíček, stojí za to spustit si Node Modules Inspector, což je Fuův nový nástroj, který vizualizuje závislosti projektu a ukazuje stav adopce ESM. Dostanete přehled o tom, kolik vašich závislostí už ESM umí, kde jsou problémové duplicity a jestli máte cestu k ESM-only reálně otevřenou.
Pokud píšete nový balíček, odpověď je přímočará: jděte rovnou do ESM. Čas, který tím ušetříte na konfiguraci duálního buildu, .d.mts souborech a řešení interop hraniček, jsou hodiny, možná dny vaší práce.
Jak to zestárlo za rok
Fuův článek vyšel v únoru 2025, tohle čtete v květnu 2026 – stojí za to krátce zhodnotit, co se z jeho predikcí povedlo. Krátká odpověď: skoro všechno, a rychleji, než sám čekal.
require(esm) přestal být experimentální a stal se plně stabilním. Fu o něm psal jako o „neuvěřitelném milníku“, když byl čerstvě odflagnutý. O rok později ho nikdo neoznačuje jako novinku, je to prostě jedna z věcí, které Node.js dělá.
Ladění komunity se výrazně posunulo. Články o JavaScriptovém ekosystému pro rok 2026 (například retrospektiva Madeline Miller) označují 2026 rovnou za „rok plné adopce ESM“. To, co v únoru 2025 znělo jako odvážné prohlášení, se stalo konsenzem.
Nástrojový ekosystém pro autory knihoven se posunul. Fu zmiňoval jako standard tsup, dnes se stejně často objevuje tsdown, který na něj přímo navazuje a těží z Rolldownu. Principy, o kterých Fu psal, zůstávají stejné, jen nástroje okolo jsou modernější.
A nakonec: Fuův Node Modules Inspector skutečně vzklíčil a dnes je to běžně používaný nástroj při auditu závislostí před migrací.
Závěr
Stojí za zmínku, že Fu píše z pozice někoho, kdo maintainuje frontendový ekosystém – Vue, Nuxt, Vite, Slidev. V enterprise Node.js světě, kde se provozují dlouhodobě stabilní služby na starších LTS verzích, bude přechod pomalejší a to je v pořádku. Argument „zůstaňme na CJS pro jistotu“ ale pomalu ztrácí platnost. Každý měsíc přibývá nástrojů, které ESM nejen zvládají, ale v něm rozkvétají.
Celý originální článek stojí za přečtení, i s odstupem více než roku. Je dobře napsaný, nebojácný, a přitom bez aktivistického tónu – spíš věcné zhodnocení stavu než manifest. A jak se ukázalo, velmi přesná předpověď.