Всем, привет! Меня зовут Данильян, я работаю в Самокате фронтенд-разработчиком и разрабатываю бэкофисное приложение с использованием React. Помимо работы, у меня есть несколько сайд-проектов, в которых я широко использую Deno. В последнее время этот проект радует новыми фичами чуть ли не каждую неделю и об одной из них я хотел бы рассказать в этом посте.

Что нового в Deno 1.28

В течение нескольких последних лет NodeJS в разрезе проблем с безопасностью и npm, в частности, не пинает только ленивый. Код зависимостей (а также зависимостей зависимостей) необходимо проверять на наличие уязвимостей и намеренно внедрённого вредоносного кода.

Возможно, некоторые знают, что Deno умеет запускать код, написанный для Node механизм интеропа (о нём ниже), но то как он это делает, заслуживает внимания обеспокоенных безопасностью, о проблемах в которой было сказано.

Что важного принёс апдейт

import { createRequire } from "https://deno.land/std@0.165.0/node/module.ts";
const require = createRequire(import.meta.url);
const path = require("path");
const npmModule = require("npm-module-name");

Однако npm-module-name должен был быть установлен с помощью npm и директория node_modules должна существовать. Можно было даже прикрутить типы с помощью специального комментария:

// @deno-types="https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/master/types/express/index.d.ts"// @deno-types="https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/master/types/express-serve-static-core/index.d.ts"// @deno-types="url/or/filesystem/path/to/typings.d.ts"// @deno-types="url/or/filesystem/path/to/typings.d.ts"

Это работает и сейчас, и этот способ не deprecated.

Однако, появился более простой способ как можно импортировать модули:

// @deno-types="https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/master/types/express/index.d.ts"// @deno-types="https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/master/types/express-serve-static-core/index.d.ts"// @deno-types="url/or/filesystem/path/to/typings.d.ts"
import npmModule from "npm:npm-module-name";

Для того, чтобы deno «понял» откуда брать зависимость, необходим префикс npm:.

Через imports_map.json тоже работает:

{
"imports": {
"lodash": "npm:lodash@^4.17"
}
}

Некоторым npm пакетам жизненно необходимо находиться в node_modules, так тоже можно:

$ deno run --node-modules-dir main.ts

В этом случае пакет будет кеширован в локальный node_modules вместо того, чтобы кешироваться в стандартную директорию кешей Deno. Если её не указывать, то пакеты npm как и обычные зависимости Deno будут кешированы в стандартную папку кешей Deno. Если указать версию, то будет использована именно она, а не последняя на данный момент. Версии, как и в случае с обычными зависимостями Deno считаются иммутабельными и не скачиваются повторно (конечно, если не указать это специально через deno cache --reload my_module.ts).

Подводные камни interoperability

Я слукавлю, если скажу, что работает абсолютно все: некоторых возможностей node до сих пор нет. Например, поддерживаются только эти «встроенные» пакеты. То есть если npm модуль использует отсутствующие возможности, то запустить код модуля с помощью deno не получится.

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

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

Какие фреймворки удалось завести, а какие нет

Тем не менее исходя из документации Deno, обеспечена поддержка некоторых очень востребованных библиотек и фреймворков. Среди них есть драйверы баз данных, например, для MySQL, PostgreSQL, MongoDB. Есть и поддержка фреймворков типа Vue и React. И кстати хайповый NextJS тут имеет свою достойную альтернативу – AlephJS, который работает без необходимости использовать npm модули, нативно.

Почему это интересно в том числе тем кто пишет на node

В самом начале статьи я писал про то, что у npm есть проблемы с безопасностью. Код может делать всё, что позволено текущему пользователю. Это и происходит в случае использования node.

В случае с Deno не всё так просто: весь код, запускаемый в модулях проекта, в модул��х зависимостей, будь то модули написанные для deno или зависимости, пришедшие из репозитория npm, наследуют права доступа, указанные при запуске скрипта. То есть мы, конечно же, до сих пор можем запустить deno с--allow-all и разрешить сразу всё, либо можем по отдельности разрешить доступ к сети или даже к конкретным адресам или к конкретным местам в файловой системе, формируя белый список разрешённых ресурсов. Всё остальное будет нельзя и при попытке обратиться к этим ресурсам deno в интерактивном режиме задаст вопрос, можно ли такое или нет.

Наглядный пример: создадим небольшой скрипт для Deno, который будет запускать простой express-сервер. Я немного расширил пример из документации, для того чтобы типы лучше резолвились, но смысл не поменялся:

// mod.ts
// @deno-types="https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/master/types/express/index.d.ts"// @deno-types="https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/master/types/express-serve-static-core/index.d.ts"
import { Express } from "npm:express-serve-static-core";
// @deno-types="https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/master/types/express/index.d.ts"
import express from "npm:express@4.18.2";
const app: Express = express();
app.get("/", function (req, res) {
res.send("Hello World");
});
app.listen(3000);
И запустим его:
$ deno run ./mod.ts

Deno скачает все зависимости, и запустит сервер, но перед этим задаст кучу вопросов о том, действительно ли этот скрипт имеет право использовать сеть и читать переменные окружения. Если бы этот скрипт считывал файлы, то о них также были бы заданы соответствующие вопросы.

Обычно проверка зависимостей проекта на уязвимости и на вредоносный код требует поднятия своей инфраструктуры, которая бы делала автоматические проверки, своих зеркал npm, в которые попадают только проверенные (в худшем случае только временем) пакеты, а также ручной аудит всего кода. Баланс между удобством автоматического управления зависимостями с помощью npm и безопасностью необходим. Это оградит от утечек персональных данных, в случае если пакет выполняет скрипт postinstall, отправляя на сторонние сервера содержимое директории .ssh, а то и вообще загружая сторонний код на сервер и выполняя его. 

Вместо итогов 

Другими словами, если верить маркетинговой статье от самого проекта deno, было «добавлено» 1.3 миллиона новых пакетов. Верить не стоит, я крайне сомневаюсь, что все 1.3 миллиона будут работать одинаково хорошо, а если и будут, качество != количество. Однако большинство из того, что используют разработчики, р��ботает и работало ещё до внедрения этой фичи.

Документация
Статья о релизе
Релиз 1.28.0

Используете ли вы Deno в своих проектах или даже в продакшене? Сталкивались ли вы с вредоносным кодом из npm? Если да, то напишите, как это проявлялось.