Як Deno 2.x тыха ўсунуў проблемы сумяшання версій Node і вялікую колькасць засобаў для роботы
У этайной стацыі рассказваеца пра выданнія Deno 2.0–2.9, паказваючы, як сумяшнасць з npm, наборы прав і вбудованыя інструменты пазбавілі працоўную сложнасць, якая раней змушвала разработчыкоў адказвацца ад яго.
Тыя пробныя запускі здарыліся пяць гадоў таму.
Месяц аднаўсі, калі патрэбны был короткі дадатковы проект, яму знадобіўся маленькі HTTP-сервер. Новы каталог, deno init, і за дзесяць хвілін ужо быў готавы працюючы сервер з TypeScript, тэстамі, форматаваннем і инструментамі для перагляду коду. Не патрэбна была пісаць файл tsconfig. Не было налажвання prettier. Не было конфігурацыі eslint. Не было файла jest.config.ts. Не было нават файла package.json.
Працэс перагляду версіі паказаў 2.9.3.
Выявілася, што з жаўтня 2024 года па ліпень 2026 года Deno выдала дзесяць мінорных апдэйтаў, і кожны з іх тылу рашаў адну з тых самэй проблем, якія спрычынілі першыя адмовы ад викорыстання цьего інструменту. Нічага з гэтага тады не было зафіксавана, таму што інструмент вже быў пасортаваны як „цікавая ідея, але не гатова для рэальнай працы“, і не было прычыны зноў на яго паглядзець.
Ёсць кароткі пераказ таго, што на самай працы змянілася, па адной старой проблеме.
Жалоба 1: пакеты npm былі другой категорыі
Гэта была прычына проблем для багато разработнікаў, укладзячы ў сабе і яго. Пад Deno 1.x любы пакет, які не быў выпусцаны на deno.land/x або не меў правільных URL-адрэсаў ES-модуляў, проста не працаваў. Разлік межу светам Deno і светам npm здаваўся вечным.
Deno 2.0, выданы ў жаўтні 2024 года, цэлкам закрыў гэты разлік. Тепер вы можете безпасабічна запрашаць будзь-які пакет з каталогу npm, які налічвае больш чым два мільйоны пакетаў, пры выкарыстоўванні спецыфікатора npm::
import express from "npm:express@5";
import { PrismaClient } from "npm:@prisma/client";
Якщо вы воліяеце настаўляцца на package.json, гэта таксама падтрымваецца. Deno парасканавае яго, запрашае залежнасці з реестру npm і нават можа стварыць локальную папку node_modules, якщо увімкнуць такое настаўленне. Прыватныя реестры таксама працуюць через .npmrc, адносна таго, як гэта робіцца ў Node.
Статыстыка, якая парадоксальна, але пераканала всіх: большэйшыя 75 процэнтав сабеўскага набора тэстаў Node тепер успешна запускаюцца пад Deno. Гэта не якісь поверхневы рашчыпак — гэта сведчыць пра практычную, пераканальную сумаспаднасць.
Момент, які паўнаста пераканаў усіх, быў калі Deno быў прыкладзены да вялікага проекту на Express. Не патрэбна была змяніць ні адной інструкцыі import. Был добавлен файл deno.json з значэнням "nodeModulesDir": "auto", была запускана команда deno install, і час запуску складаў або-та 900 мс, у працоўных умовах npm гэты час станавіў пад 3 секунды (тэставана на проекте, які выкарыстоўваў React, Vite, Babel і ESLint, з чыстым кэшам). Сервер запускаўся правільна ўжо пасля першай спробы.
Адзін з працоўных прытвароў: система праваў была клопатная ў практыцы
Канцэптуальна модель праваў была логічная. Але на практыцы гэта значыла неабходнасць перапісвання падобных інструкцый ў кожны раз, калі запускалася програма:
deno run --allow-read=./data --allow-write=./data --allow-net=api.example.com --allow-env main.ts
Як толькі забыць адзін флаг, процес завершаецца. Як толькі дадзіць новую залежнасць, яка чытае значэння з мэтры працы, процес знову завершаецца. Гэта больш садоўваеся да налаштавання правіл фаервалу, чым да стварэння дадатковага проекта.
Deno 2.5, выданы ў сэрпені 2025 года, адкрыў можлівасць насталявання набораў дазволаў прымусова ў файле налаштавання. Вы прызначаеце іменаваныя наборы пад ключам "permissions" у файле deno.json, а пасля выкарыстоўваеце іх за дапамогою флага -P (скрацанне для --permission-set):
{
"permissions": {
"default": {
"read": ["./src", "./data"],
"write": ["./data"],
"net": ["api.example.com", "0.0.0.0:3000"],
"env": true
},
"test": {
"read": true,
"write": ["./tmp"],
"net": false
}
}
}
З таго моменту, калі запускаецца deno run -P main.ts, автаматычна выкарыстоўваецца „стандартная“ настаўка з файла deno.json, які знаходзіцца ў каталозе працы, але можна таксама запускаць deno test -P=test, ў тым случае будзе выкарыстоўвана аднаццая настаўка, прызначаная толькі для тэстав. Код тэстаў і код, які выкарыстоўваецца у працэсе роботы, маюць разныя правыя прывілеі без неабходнасці змены хоць якога параметра у командной лініи. Асновныя захоцы безпекі не зменіліся — проста зникла вся тая сложнасць, якая была ранейш.
Таксама існуе зменная сераўіса DENO_AUDIT_PERMISSIONS, якая стварае лог у формате JSONL, які фіксуе кожную перацэнку прав, якая выканана пад час роботы програмы. Чэрез гэта можна точна бачыць, да чаго намагаюцца дасягнуць вашы залежнасці, не прабуючы чытаць ўсё іхнё выхідны код.
Жалоба 3: Мне все равно была патрэбна купа дадатковых інструментаў
Благаслованнее і адночасна няга Node — гэта тое, што практычна кожная адпаведальнасць перакладаецца на адзін окремы пакет. Патрэбна форматаванне? Выберіце prettier. Праця з памылкамі? ESLint і калькі плагінаў. Тэставанне? Jest або Vitest, плюс файл налашоўкі для обробкі трансфармацый TypeScript. Перакананне типаў? tsc, налаштаваны незалежна ад таго, што запускае ваш код. Аб’еднаванне? Выберіце ад паловы дзюжыны інструментаў для аб’еднавання, кожны з якіх мае свой сабскі дыялект налашоўкі.
Deno аб’еднвае всё гэта ў вбудованыя падкаманды. Яны існавалі ўжо да выходу версіі 2.0:
deno fmt # formats TS, JS, JSON, HTML, CSS, YAML, SQL
deno lint # built-in linter with quick fixes
deno test # test runner with coverage, snapshots, sharding
deno check # type checking
deno compile # single binary, cross-platform, code signing
deno bench # benchmarking
deno doc # documentation generation
Пачынаючы з версіі 2.8, з’явілася ўсьмо шэсць дапаможных падкаманд:
deno audit # security audit of dependencies
deno audit fix # auto-upgrade vulnerable packages to nearest patched version
deno why # explain why a package is installed (traces dependency paths)
deno transpile # strip types, emit .d.ts declarations
deno pack # build an npm-publishable tarball from JSR/Deno code
deno ci # reproducible install for CI (errors without lockfile)
А з версіяй 2.9 прыбавілася ўсьмо больш:
deno desktop # build native desktop apps via webview (experimental)
deno list # show dependency tree (like npm ls)
deno link/unlink # local package linking for development
Найбольша популярна аплікацыя — deno compile. Стварэнне невеликага інструмента кліентскай лініі та запуск команды deno compile --target x86_64-unknown-linux-gnu main.ts дае самостатны бінарны файл. На цэльнай машыне не трэба нічога іншаго інсталаваць — проста копіюйце яго на сервер і запускайце.
Увага: створаны бінарны файл з’яўляецца з V8 і всім рантаймам Deno, таму стандартныя прыклады програм маюць размах ад 60 да 100 МБ. Калі важліва габарытна ўмаўкненасць, можна викорыстаць
deno compile --bundle(які ў версіі 2.8 яшчэ нестабільны), які за дапамогою агрэсыўнага спалучання элементаў можа значна зменшыць размах для простых скрыптав. У сабеўнім блогу Deno паказана, як прыклад lodash „hello world“ за дапамогою параметраў--bundle --minifyмае размах 1,5 МБ.
Жалоба 4: міграцыя рэальнага проекту здавалася недаступной
Сяроджытныя пераканалкі для змены часу выконання зазвычай не маюць нічога спакульнага з самым часам выконання. Це файл блакавання, структура node_modules, ад каторай залежаць вашы існуючыя інструменты, а таксама розрасцеленыя вызовы require() у всім кодавой базе.
Deno 2.9 адкрыў можлівасць стварэння файла блакавання. Падазроўваючы, што проект вялікі час адстэйвае свае залежнасці за дапамогою аднаго з распашчытных файлаў блакавання калектара пакетаў — напрыклад, package-lock.json ад npm, pnpm-lock.yaml, yarn.lock або bun.lock — але ніколі не стварыў deno.lock, запуск deno install у такім проекте стварае нехватны deno.lock адразу з таго файла, які ён знаходзіць. Версіі, якія былі выкарыстоўваны, збігаюцца. Хэшы целаснасці таксама збігаюцца. Не трэба хваліцца пра якія-небудзь адхыленнія ў вырашэнні.
Для ситуацыяў, дзе практычна неабходны фізычны каталог node_modules — натыўныя дадаткі або інструменты, якія безпасередна скануюць файлавую систему — настаўка "nodeModulesDir": "auto" прыказвае Deno стварыць такі каталог. Таксама існуе опцыя hoisted-layout ("nodeModulesLinker": "hoisted" у deno.json, доступная з версіі 2.8) для старэйшых інструментаў, побудаваных на плоскай структуре каталогаў npm уместо сімвольных звязкаў у стыле pnpm.
Node shim — гэта цікавая функцыя: як толькі вы запускаеце сам Deno, ён автаматычна інсталюе бінарны файл-заменяльнік node у ваш PATH, без якой-лібо ручной настройкі. Як толькі нічога іншаго ўжо не запускае виконвальны файл node, гэты shim перехопляе запускы Node CLI, перакладае аргументы і передае іх Deno. Это значыць, што скрыпты CI, якія ўсё ўсё запускаюць node dist/server.js, продовжаюць працаваць без жадных змян. Вы можете выключыць гэту функцыю за дапамою DENO_DISABLE_NODE_SHIM=1, якщо не хочаце, каб гэта відбывалася автаматычна.
Імпорты з простым спецыфікаторам — напрыклад, import fs from "fs", якія перетвараюцца на node:fs — былі дадзены ў версіі 2.0 і сталі абсолютна стабільнымі, працуючы без жадных флагоў чы настройкаў, пачынаючы з версіі 2.9. Не трэба перапісваць вашы оператывыя запісы імпорту, каб перайсці на гэты формат.
Праба перайсці
Разглянем просты проект Hono API (Hono — это легкія фрэймворк HTTP, падобны Express), які мае або 15 маршрутаў, базу дадзеных Postgres, да якой выходзіць через Drizzle ORM, і працэсар фонавых заданняў. Ён работаў на Node 22 з падмагай пакета pnpm. Усе эксперыменты былі адбываліся на Deno 2.9.3.
Ось як праходзіла перакладка:
- Запускайце команду
deno installз корневага каталога проекту. Яна за менш чым два секунды стварае файлdeno.lockна адной з базы існуючага файлаpnpm-lock.yaml. - Дадзіце значэнне
"nodeModulesDir": "auto"у што толькі створаны файлdeno.json. - Запускайце прыложэнне за дапамою команды
deno run -A src/server.ts.
Усе працюе чыста. Кожны з 15 маршрутаў рэагуе правільна, запыткі на аднойчынцы выконваюцца як і планавалася, а працоўнік кялей продовжае обрабляць задання на фоне.
Аднак зламаліся тры рэчы:
- Адны з файлаў для тэставвання выкарыстоўваў
jest.mock(), якога няма ў вбудованым запускачы тэстаў Deno. Замена яго на ручны мок займае менейш чым пяць хвілін. - Адна з залежнасцей выкарыстоўвала
__dirnameу файле CommonJS, але Deno запускаў яго як ES-модуль. Рашэнняя было ў тым, каб дадаць"type": "commonjs"у локальныя настройкі гэтага пакета.
process.env.NODE_ENV без явнага імпорту process. Што стосуецца гэтай часткі, яна фактычна працавала нормальна, адколькі з версіі 2.0 Deno выклікае process як глобальную змяню — але флагі дазволаў для гэтага конкретнага скрыпта не мелі параметра env: true, таму яго было трэба дадаць.Весь процес перакантролювання зайняў прыблізна 25 хвілін з пачатку да канца. Час запуску з нуля зменіўся з апошніх 620 мс да 320 мс, за данымі змераў з апранткам hyperfine пад час 50 запускаў. Выкарыстоўванне памяці у стане без дзейнасці (RSS) знизілася з 142 МБ да 64 МБ. Крэм таго, можна было выдаліць чатыры окалічныя файлы налаштавання: prettier, eslint, jest і tsconfig.
Ўсё ўсё застаецца неідеальна
У заглавлі гэтага артыкула згадваецца „всё, чыя я ненавідаў“, а выказаныя раней працоўкі былі справжнімі, распашчастымі проблемамі сярод разрабоў Node. Тым не менш, існуе калькі новых аспектаў, якія варта выказаць:
- Павнота падтрымкі API Node ня ўсёвыключнае. 75 процэнтов пасэкі ў тэставальнай суцэльцы Node значыць, што чатвёртая частка яе ўсё ж не праходзіць. Гэтыя недастаткі можа не паўлінуць на звычную навантажэння, але тыя, хто выкарыстоўвае нестандартныя аспекты
node:vm, програмнае выкарыстоўваннеnode:inspectorабо болей глыбокія функцыіnode:cluster, должны з’явіцца на панелі сумаспадобнасці на node-test-viewer.deno.dev прычаму.
node_modules. Усе, што выкарыстоўвае біндынгі на C++ — такія пакеты як sharp, bcrypt, sqlite3 і адпоўнэ заіхнутыя — трэбуе як настройкі nodeModulesDir, так і флага --allow-ffi. Цэе працюе, але ўсё ж такі являе сабою дадатковы крок налаштавання, які лёгка адмахнуцца з-за недбаласці.postinstall — напрыклад, крокі компіляцыі через node-gyp або prisma generate — вымагаюць явнай дазволу за дапамою параметра --allow-scripts=npm:package-name. Цэа ўсвядомленая захоўнай мера, але гэта можа застаць команду зненацька пад час міграцыі.process.versions.node; іншыя прылады ж стаўляюць фіксаваныя шляхі да файлаў, напрыклад /node_modules/.cache. Схема модуляў Deno у стыле pnpm з сіндыкаванымі паўзамкамі стварае проблемы для дзяўяносці такіх інструментаў. Ўсіме існуе опцыя «hoisted-mode», якая дапамагае абйść гэтыя проблемы, але гэта толькі тымчасова рашэння, а не справжняя адпаведнасць.Ніякі з гэтых проблем не быў настолькі серьёзны, каб перакрыць вышэй описаную міграцыю. Аднак для іншых проектаў гэта можа стаць прычынай проблем, таму варта пераканацца ўсё заздалегідь, а не адкрываць іх падчас самай міграцыі.
Чаму гэтыя практычныя рашэння засталіся непазначанымі
За 21 месяц было выдана дзесять версій з малыми апдэйтамі, кожны з якіх тыха ўсунуў тры-пяць проблем. Не было жаднага значныя перапісвання коду, жадных заходаў з адкрыццю „Deno 3.0“. Команда запрацавала систему дзеярожнага размежавання прав у версіі 2.5, даслагла до швайнаеўства інсталяцыі на рэвэлі npm у версіі 2.8, а таксама дадала можласць стварэння файлу lockfile у версіі 2.9, спрыяючы гэтаму як часткі звычайнага тыпавання, а не як важлівай новасці.
Пораўняйце гэта з тым, як зазвычай аанунсуюць пераходы на новыя фрэймворкі: пастая ў блогу, прымова на конферэнцыі, кансультатывныя інструкцыі па міграцыі, хвіля каментароў у сацыяльных меражах, агрэсывныя думкі і заперчанні на яе. Deno адмовіўся ад усього гэтага цыклу публічных дыскусый і проста выдаваў патрабованыя правкі безпосередна.
Разработчыкі, якія рана не прынялі гэты runtime, часта робілі гэта таму, што сформавалі свою думку раз і больш не пераглядалі яе, калі інструменты ставалі болей зрэлымі. Гэта ўпуск увагі, а не недастатак самога проекту.
Для тых, хто склікваўся аб Дено ранейш, чым з’явілася версія 2.0, інструмент, які тады теставалі, практычна зник. Тое, што існуе сэрцы, меньш нагадвае «цікавы рантайм TypeScript, які не можа працаваць з npm», а больш — «Node без усіх його додатковых элементаў». У тыя часы разчарованні былі правамернымі. Їх вже рашылі. Варта ўважна паглядзець зноў.
Спадневая літэратура
- Замена Jest на вбудованага тэст-раннера Node у Node 24 — Практычны прыклад міграцыі паказвае, як вбудованы тэст-раннер Node 24 і падтрымка TypeScript дапамагаюць скорачыць час CI, адмахнуўшыся чатырох залежнасцяў.