Что такое Deno и чем этот проект отличается от Node.js?

Автор оригинала: Maciej Cieslar
  • Перевод
Райан Даль, создатель Node.js, потратил последние полтора года на работу над проектом Deno. Это — новая среда выполнения для JavaScript, которая должна исправить проблемы, присущие Node.js.

Не поймите меня неправильно. Платформа Node.js представляет собой замечательную серверную среду для выполнения JavaScript. Своей популярностью она обязана, преимущественно, огромной экосистеме, и, собственно, поддержке JavaScript. Однако Райан Даль признаёт, что кое-чему, касающемуся Node.js, ему стоило бы уделить больше внимания. Речь, в частности, идёт о безопасности, о модулях и об управлении зависимостями.


В его защиту можно сказать то, что он не мог знать о том, насколько популярной станет платформа Node.js за довольно короткий отрезок времени. Кроме того, в 2009 году JavaScript всё ещё выглядел как ограниченный и странный язык, над которым издевались все, кому не лень. Также надо отметить то, что в те времена многих возможностей JavaScript, привычных в наши дни, ещё не существовало.

Что такое Deno и каковы основные особенности этой платформы?


Deno — это безопасная среда выполнения TypeScript, построенная на базе JS-движка V8, разработкой которого занимается Google. Платформа Deno создана с использованием следующих средств:

  • Rust (ядро Deno написано на Rust, а ядро Node — на C++).
  • Tokio (цикл событий, написанный на Rust).
  • TypeScript (Deno, без дополнительных настроек, поддерживает и JavaScript, и TypeScript).
  • V8 (JS-движок от Google, используемый в браузере Chrome, в Node.js и в других проектах).

Поговорим о том, какие возможности предлагает нам платформа Deno.

Безопасность (разрешения)


Среди наиболее важных возможностей Deno, которым уделяется особое внимание, можно отметить безопасность.

В отличие от Node, Deno, по умолчанию, выполняет код в «песочнице». Это означает, что у среды выполнения нет доступа к следующим сущностям и возможностям:

  • Файловая система.
  • Сеть.
  • Выполнение других скриптов.
  • Переменные окружения.

Взглянем на то, как работает система разрешений Deno. Рассмотрим следующий скрипт:

(async () => {
 const encoder = new TextEncoder();
 const data = encoder.encode('Hello world\n');

 await Deno.writeFile('hello.txt', data);
 await Deno.writeFile('hello2.txt', data);
})();

Этот скрипт создаёт пару текстовых файлов — hello.txt и hello2.txt. В эти файлы помещён текст Hello world. Код выполняется в «песочнице». Поэтому у него нет доступа к файловой системе.

Кроме того, обратите внимание на то, что тут мы используем пространство имён Deno, а не обращаемся к чему-то вроде модуля fs, как сделали бы в Node. Пространство имён Deno предоставляет в распоряжение разработчика множество базовых вспомогательных функций. Но мы, используя пространство имён, теряем совместимость кода с браузером. Об этом мы поговорим ниже.

Этот скрипт можно запустить такой командой:

deno run write-hello.ts

После этого мы увидим уведомление следующего содержания:

Deno requests write access to "/Users/user/folder/hello.txt". Grant? [a/y/n/d (a = allow always, y = allow once, n = deny once, d = deny always)]

На самом деле, мы вполне можем увидеть это дважды, в ходе выполнения каждого из вызовов. Конечно, если мы ответим на вопрос системы, выбрав опцию allow always, второй раз нам это уведомление выдано не будет.

Если же мы выберем один из вариантов deny — будет выдана ошибка PermissionDenied. Процесс скрипта будет после этого завершён, так как в нём нет кода для обработки ошибок.

Скрипт можно запустить и так:

deno run --allow-write write-hello.ts

Уведомлений при этом мы не увидим, оба файла будут созданы.

Помимо флага --allow-write, влияющего на работу с файловой системой, при запуске скриптов можно использовать и другие флаги. Это — --allow-net, --allow-env и --allow-run. Они, соответственно, открывают скрипту доступ к сети, к окружению, и к запуску подпроцессов.

Модули


Deno, как и браузеры, загружает модули по URL. Многих поначалу путает то, что они видят в серверном коде команды импорта с URL. Но в этом, на самом деле, есть смысл. Подождите немного — и вы это поймёте сами.

import { assertEquals } from "https://deno.land/std/testing/asserts.ts";

Тут у вас может возникнуть вопрос о том, что такого особенного в импорте пакетов по URL? Ответ на этот вопрос прост: благодаря использованию URL пакеты Deno могут распространяться без использования централизованного реестра наподобие npm. У npm в последнее время возникает множество проблем.

Организация импорта кода посредством URL позволяет создателям пакетов хостить свой код везде, где они сочтут нужным. Это — децентрализация во всей красе. Больше никаких package.json и node_modules.

Когда мы запускаем приложение, Deno загружает все импортированные модули и кэширует их. После того, как они оказываются кэшированным, Deno не будет повторно их загружать, если только мы явно не запросим их повторную загрузку, использовав флаг --reload.

Относительно этой системы работы с модулями можно задать несколько важных вопросов.

▍Что если ресурс, на котором размещён код модуля, окажется недоступным?


Код модулей хранится не в централизованном реестре. Некий веб-ресурс, на котором находится этот код, может оказаться недоступным по множеству причин. В процессе разработки, или, ещё хуже, выводя проект в продакшн, рискованно надеяться на то, что хостинг модуля будет всегда доступным.

Как уже было сказано, Deno кэширует загруженные модули. Так как кэш хранится на локальном диске, создатель Deno рекомендует обрабатывать его средствами системы контроля версий (то есть — git) и включать в репозиторий проекта. При таком подходе, даже когда ресурс, на котором хранится код, окажется недоступным, все разработчики проекта сохранят доступ к загруженным версиям модулей.

Deno хранит кэш в папке, заданной переменной окружения $DENO_DIR. Если мы эту переменную не настроим — Deno будет хранить кэш в стандартной системной директории для кэшей. Переменную $DENO_DIR можно установить так, чтобы она указывала бы на какую-нибудь папку в нашем локальном репозитории. Эту папку можно обрабатывать с помощью используемой системы контроля версий.

▍Нужно ли будет постоянно импортировать модули по URL?


Регулярный ввод длинных URL может быстро надоесть. К счастью, Deno даёт нам два способа облегчить выполнение этой задачи.

Первый способ заключается в использовании возможности повторного экспорта импортированного модуля из локального файла. Например, это может выглядеть так:

export { test, assertEquals } from "https://deno.land/std/testing/mod.ts";

Предположим, что тот файл, в котором находится вышеприведённая команда, называется local-test-utils.ts. Теперь, если нам снова понадобятся функции test или assertEqual, мы можем импортировать их так:

import { test, assertEquals } from './local-test-utils.ts';

В результате оказывается, что неважно — был ли модуль загружен по URL или нет.

Вторая возможность заключается в создании карты импортов, оформленной в виде JSON-файла:

{
   "imports": {
      "http/": "https://deno.land/std/http/"
   }
}

При использовании подобного файла команда импорта может выглядеть так:

import { serve } from "http/server.ts";

Для того чтобы эта схема заработала — нужно сообщить системе об использовании в проекте карты импортов, использовав при запуске скрипта флаг --importmap:

deno run --importmap=import_map.json hello_server.ts

▍Как осуществляется управление версиями модулей?


За управление версиями пакетов отвечает их разработчик. С точки зрения клиента выбор нужной версии пакета выглядит как его добавление в URL:
https://unpkg.com/liltest@0.0.5/dist/liltest.js.

Совместимость с браузерами


Платформа Deno стремится к совместимости её кода с браузерами. С технической точки зрения, мы, когда используем ES-модули, не обязаны применять некие сборочные инструменты, наподобие webpack, обеспечивающие возможность запуска приложения в браузере.

Однако инструменты вроде Babel преобразуют современный JS-код в код, соответствующий стандарту ES5. В результате этот код может выполняться даже в не самых новых браузерах, не поддерживающих современные возможности JavaScript. Но и у такого подхода есть минус, который заключается в том, что бандлы веб-проектов разрастаются из-за того, что в них попадает много вспомогательного кода.

Собственно говоря — решения о целях наших проектов принимаем мы. Мы же выбираем и соответствующие инструменты.

Поддержка TypeScript


Deno упрощает использование TypeScript, избавляя разработчика от необходимости что-либо настраивать для запуска TS-кода. Но программы для Deno без проблем можно писать и на JavaScript.

Итоги


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

Децентрализованный подход к работе с модулями, реализованный в Deno, направлен на то, чтобы освободить экосистему JavaScript от централизованного хранилища пакетов, которым в наши дни является npm.

Райан Даль говорит, что он рассчитывает выпустить Deno 1.0 в конце лета. Если вас интересует будущее этого проекта — обратите внимание на его репозиторий.

Уважаемые читатели! Что вы думаете о Deno?

RUVDS.com
862,78
RUVDS – хостинг VDS/VPS серверов
Поделиться публикацией

Комментарии 43

    +17
    Идея с импортом пакетов откуда угодно — как-то сомнительно выглядит, как по мне. В случае с NPM у нас есть какая-никакая, но уверенность что там будет именно то, что мы ожидаем. А в случае с deno — 5 раз загрузишь в новые проекты нужную тебе библиотеку, а на шестой раз окажется что тот сервер взломали, и ты подгрузил себе криптомайнер. В итоге всё скатится к тому, что все библиотеки будут в основном скачивать, и хранить где-то у себя. А потом ставишь какой-то старый проект с гитхаба (который уже давно забросили, но он тебе сильно нужен) — а в нём половина зависимостей уже померли. Хранить их все в своём гите? Ну такое, похоже на шаг назад, к временам когда проекты весили по несколько гигабайт, хотя своего кода там на сотню метров. А так… Зачем нам вообще тогда импорты извне, давайте просто сгребать всё в одну папку и оттуда тянуть.
      +1
      На самом деле ситуация будет такая же как и у Go, так же по url грузишь зависимости из кода (без там всяких go mod, dep и т.п.) и используешь из локальной папки. Единственное отличие в том что в Deno они грузятся при первом запуске, а не как в Go, все таки он компилируемый.
        –2
        А в случае с deno — 5 раз загрузишь в новые проекты нужную тебе библиотеку, а на шестой раз окажется что тот сервер взломали, и ты подгрузил себе криптомайнер.

        Хэш-суммы придумали не вчера.
          +6

          А где в строчке import { assertEquals } from "https://deno.land/std/testing/asserts.ts"; вы видите хеш-сумму?

            0
            Нигде. И? Я и продукта-то пока не вижу, однако это не мешает писать статьи и обсуждать. В HTML integrity тоже не с версии 1.0 появилась.

            Речь про то, что вообще-то методы повышения доверия к скачиваемому непонятно чему непонятно откуда — вполне существуют.

            ЗЫ: Ну и мысль заглавного коммента о том, что NPM «лучше» — она тоже заслуживает осуждения. Не лучше. Всё те же яйца.
              +1
              Речь про то, что вообще-то методы повышения доверия к скачиваемому непонятно чему непонятно откуда — вполне существуют.
              Самый лучший метод повышения доверия к непонятно чему — не скачивать непонятно откуда.
                0
                Самый лучший метод повышения доверия к непонятно чему — не скачивать непонятно откуда.

                Конечно. Но удобство скачивания настолько велико, что оно так или иначе никуда не денется; исключая случаи, где повышенный уровень недоверия просто необходим по предметной области.
                  +5
                  Всё равно, хоть убейте, не понимаю, в чем собственно удобство-то? Размазать зависимости ровным слоем по всему проекту, чтобы было заранее неизвестно, что этой штуке нужно для запуска? Не иметь отдельной управляемой фазы установки? Сломать все инструменты статического анализа, не умеющие ходить по прямым ссылкам?
        +11
        Deno, как и браузеры, загружает модули по URL
        Как я уже писал и в прошлый раз, загрузка исходников по прямым ссылка фактически в рантайме — это умножение на 0 всех потуг в стабильность и безопасность.
        Ну и до кучи, подгружать так можно только отдельно лежащий файл в вакууме. Ни зависимостей ни деклараций вы таким образом не получите.
          +11
          А ещё такой подход наглухо лишает нас возможности автоматического обновления всех пакетов. Захотел что-то обновить — иди правь все импорты на "....ts?v2.0.3", или какое-нибудь "@2.0.3/script.ts", потом правь их зависимости, а потом проверяй где и что отвалилось, потому что оно несовместимо с твоей версией окружения. Уже не говоря о том что нельзя просто указать свою версию ноды, и поставить пакеты которые с ней совместимы. Жуть, я не представляю как этим можно будет пользоваться.
            0

            В статье упоминается флаг --importmap, с помощью которого можно настроить маппинг коротких имен модулей на полноценные урлы. В таком случае в исходниках будут такие же как и сейчас import 'lodash';, а обновление версий будет происходить только в файле маппинга.


            Более того, import-maps является кандидатом в веб-стандарты и уже имеется некоторый набор инструментов, чтобы генерировать import-maps из yarn.lock or package-lock.json

              0
              Это хоть и стандарт, но стандарт костыля для браузера. И он предназначен для импорта файлов, а не пакетов.
                0

                Deno декларирует совместимость с браузером в своих design goals, так что ничего удивительного в том, что они используют стандарт написанный для браузера.


                В import-map есть возможность указать корневой путь сразу до всех файлов из модуля:


                {
                  "imports": {
                    "lodash/": "/node_modules/lodash-es/"
                  }
                }

                вот вам и импорт пакетов.

                  +1
                  Deno декларирует совместимость с браузером в своих design goals,
                  троллейбус_из_хлеба.jpg. В смысле, а нафига?
                  В import-map есть возможность указать корневой путь сразу до всех файлов из модуля:
                  Да, именно файлов. Без собственных зависимостей, метаданных и неявно подгружаемых элементов. Для скомпилированных библиотек в браузере — понятно зачем. Для исходников на сервере — непонятно, а кому от этого будет польза.
                    0

                    То что это не нужно вам, это не значит что это не нужно никому.


                    Унификация браузерного и серверного кода позволить использовать любые библиотеки в своих проектах, а не только специальные "изоморфные".


                    Node.js тоже движется в этом направлении, например ввели глобальный класс URL совместимый с браузером, теперь думают над добавлением нативного fetch. Просто у node большой груз обратной совместимости с существующими модулями, поэтому там это не так революционно.

                      +1
                      Тут дело-то в том, что в node.js уже есть инструменты, выполняющие все заявленные функции, притом делающие это лучше. Зачем добавлять себе проблем, чтобы потом героически их решать?

                      Унификация браузерного и серверного кода позволить использовать любые библиотеки в своих проектах, а не только специальные «изоморфные».

                      1. Подход Deno не расширяет возможности браузера до сервера, а сужает возможности сервера до браузера.
                      2. У сервера и браузера разный рантайм, большинство кода в принципе не сможет работать и там и там.
                      3. «Унификация» приведёт к несовместимости проектов Deno с node.js, что тоже мало кому надо.
                        +1
                        У сервера и браузера разный рантайм, большинство кода в принципе не сможет работать и там и там.

                        У меня почему-то выходит наоборот. Львиная доля кода — абстрагирована от рантайма.
                          0

                          … и, опять-таки, никакой deno вам для этого, скорее всего, не понадобился.

                            0
                            Ну, в общем-то да. Хотя, мне интересно, чем это кончится.
          +8
          На самом деле, мы вполне можем увидеть это дважды, в ходе выполнения каждого из вызовов. Конечно, если мы ответим на вопрос системы, выбрав опцию allow always, второй раз нам это уведомление выдано не будет.
          Устанавливаешь себе кучу пакетов, запускаешь, получаешь 20 раз запросы на разрешение. Задрало жать 20 раз «y» при каждом запуске — добавляешь флаг allow always. В итоге получается та же ситуация что и с nodejs сейчас — неизвестно что, где, и как используется. Гораздо лучше было бы иметь возможность конфигурационного файла (типа package.json) где каждому пакету есть возможность прописать его разрешения.
            0

            Мне вообще идея не понятна. Я бы хотел предложить чтобы оно работало вообще в песочнице, но докер уже изобрели. А про разрешение записи в файл — это бред. Если кому-то будет нужно, он просто сделает process.exec('echo somthingBad > /usr/var/...').

              0
              «Разрешить приложению запуск внешнего процесса? — Да / Нет»
            +1
            Помимо флага --allow-write, влияющего на работу с файловой системой, при запуске скриптов можно использовать и другие флаги. Это — --allow-net, --allow-env и --allow-run

            Если существует --allow-run то какой смысл в остальных флагах? Или я чего-то не понимаю? Если кто-то нехороший захочет дел натворить, разве --allow-run не будет достаточно?
              0
              Ну, типа предполагается что поставив себе откуда-то библиотеку, ты будешь проверять при запуске чего она там просит. Мол поставил библиотеку для красивого вывода в консоль — а она в сеть щимится зачем-то. Нехорошо. Но, это в идеальном мире. А на практике — я думаю будет вот так
                0

                Это же, разрешительный флаг, а не запретительный, в смысле детализация других "мелких" разрешений вполне законна. Не надо же сразу всем 0777 делать.

                  +1
                  Моя претензия, что deno не трушная песочница. --allow-run по факту заменяет все флаги.
                  То есть достаточно создать злой пакет, который якобы служит для запуска какого-то процесса с определенными параметрами (в npm таких неимоверное множество), но под капотом он сможет делать вообще все что угодно, и это будет не отследить.
                  Если позиционировать себя как песочницу, то стоит идти в направлении докера, sandboxie и т. д. а не просто ограничить вызовы стандартной библиотеки.
                +6
                В Go собрали кучу проблем связанных с «удобным импортом прмяо из гитхаба», и в итоге пришли к модулям…
                  +6

                  Есть еще один интересный момент. Дено требует импорт с явным указанием расширения файла, например так:


                  import {file} from './files.ts'

                  Что приводит к двум проблемам:


                  • IDE начинает ругаться (для VS Code решается плагином)
                  • Критично — нельзя без апдейтов мигрировать код из/в Дено

                  Если кому сильно интересно, вот аргументы разработчиков

                    0

                    Интресно было бы узнать о скорости работы нового решения, но всех только за импорты борются.

                      0

                      В апреле Раян говорил, что они пока не дотянули до показателей node. Но в планах выйти на те же цифры или выше до мажорного релиза.

                        +3

                        Выше за счёт чего?

                          +8

                          За счёт Хайпа (простите не удержался)

                            0

                            Я мог бы написать, что за счет новой архитектуры, которая учла все прошлые наработки или упора на производительность (нет), но конкретной информации просто нет. И я не уверен что она есть у кого-то кроме разработчиков платформы. Поэтому я могу лишь повторить те обещания которые услышал.

                        +1
                        Кто сказал ALLOW_URL_INCLUDE?
                          +1

                          Это вполне могло бы сойти за тонкую первоапрельскую шутку.

                            0
                            Немного не понял. Если есть возможность импорта модулей «из сети» — а в модулях как в node есть возможность подключения native кода — что мешает подключить модуль, который будет реализовывать любой функционал в обход песочницы?
                            import myFS from 'mysite.net/myFS'
                            
                            console.log(myFS.readFromFile('anyfile'))
                            
                            

                            Доступ к файловой системе, доступ к сети и тп. Механизма контроля подключенных модулей я не увидел.

                            Вообще, изоляция на уровне контейнера типа докера выглядит более надежной
                              +2
                              Качать зависимости по ссылкам и при этом оборачивать серверный код в песочницу — это какой-то новый уровень беспомощности и современных разработчиков. Открытое признание: «Я не понимаю что как работает на моём же сервере, мне лень разбираться с настройкой репозитриев, я просто хочу копировать код со StackOverfrlow и чтоб всё работало».
                                0

                                Ну что вы к менеджеру пакетов прицепились, он тут не важный...


                                Если новый http не будет лучше сделан, чем у ноды, то этот движок не кому не нужен будет.
                                В текущей ноде проблема в медленном парсере, парсит все, даже когда это вам не нужно. Дино наступает на те же грабли.


                                Кстати след релиз ноды будет через 3 месяца. В этом должны выпустить tls для 12, по крайней мере было обсуждение.

                                  0
                                  В Node 12 будет новый более быстрый HTTP парсер llhttp
                                  blog.logrocket.com/node-js-12
                                    0
                                    Поправочка: в Node 12 уже новый парсер. Актуальная версия Ноды 12.6.0
                                      0

                                      Все же не достаточно быстрый если сравнивать с uws

                                        0

                                        Подозреваю, что он быстрый за счёт того, что там реализовано только ограниченное подмножество http. Не очень доверяю его бенчмаркам.

                                    0
                                    Не хотелось бы еще вторую ноду, которая такая же, но не совсем она и что бы один нужный мне пакет был уникален для одной системы, а второй для другой.
                                    Я понимаю ещё, если бы она была быстра как пуля и надежна как сундук с баксами, но судя по всему это совсем не так.

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

                                    Самое читаемое