Я верю, что когда-нибудь Deno мог бы стать следующим стандартом после Node, как TS постепенно заменяет JS. Экосистема ноды уже достаточно разрослась, чтобы стало сложно вносить глобальные изменения в ядро, выход 15 версии тому пример. Помните, какое там ключевое изменение? Теперь unhandledRejection вместо warn будет выдавать throw, вау! На эту тему сломано много копий, но большинство обсуждений приходит к выводу что Node оказался в стагнации. И тут Райан Даль, создатель ноды, врывается на рынок со своим свежим и гибким решением: строгий подход к безопасности, новая кодовая база на TS, новые фичи… Но есть один нюанс.

Deno пока ещё не получил безусловной поддержки среди разработчиков, и не столько из-за спорных архитектурных решений (в основном это холиварный import по URL), сколько из-за несовместимости с модулями Node. Никому не интересно переписывать с нуля все свои зависимости, и тем более, строить велосипеды чтобы обойтись без них. Проект поменьше на этом бы и умер, ведь одна из основных сильных сторон разработки на Node — это возможность моментального прототипирования чего угодно: подключил модуль, вбил пару команд и у тебя уже готов весь бойлерплейт и запущен dev-сервер.

Встроенный способ


Для поддержки частичной совместимости в Deno добавили библиотеку node, это слой над Node 12 standard library. Совместимость с API пилится неторопливо, сейчас доступны следующие модули:

  • buffer
  • events
  • fs (частично)
  • module
  • os (частично)
  • path
  • process (частично)
  • querystring
  • timers
  • util (частично)
  • node globals (частично)

Для сравнения, вот остальные:

  • assert
  • child_process
  • cluster
  • console
  • crypto
  • dgram
  • dns
  • http
  • http2
  • https
  • net
  • perf_hooks
  • readline
  • repl
  • stream
  • string_decoder
  • sys
  • tls
  • tty
  • url
  • vm
  • worker_threads
  • zlib

Кроме того, есть createRequire(), возвращающая недоступную функциональность require (!). Понятно, что это вынужденное решение, но выглядит оно как эталонный костыль:

import { createRequire } from "https://deno.land/std@$STD_VERSION/node/module.ts";

const require = createRequire(import.meta.url);
// Loads native module polyfill.
const path = require("path");
// Loads extensionless module.
const cjsModule = require("./my_mod");
// Visits node_modules.
const leftPad = require("left-pad");

Соответственно, можно импортировать любые модули хоть прямо из NPM, если они не используют неподдерживаемые API из списка выше. Что снова сильно ограничивает нас в выборе. Кроме того, у вас не будет типов, вернётся ненавистный node_modules и потребуется выдать права --allow-read.

Denoify


Для упрощения перевода модулей появился проект Denoify. Это инструмент для сборки Deno-совместимого модуля из кода на TS. Любой модуль он тоже не сможет конвертировать, потому что тоже не может обеспечить поддержку всех Node API, но по крайней мере добавляет базовые ценности вроде https и net. Разработчики стараются увеличить покрытие API, и у них это пока получается быстрее, чем у Райана.

Повторюсь, Denoify работает только с typescript. Перевести Node-модуль с JS на TS он за вас, очевидно, не сможет, поэтому эту часть работы придётся делать вручную. Нюансы легко нагуглить, но в большинстве случаев достаточно сменить расширение на .ts и раскидать any, следуя по пути, намеченному компилятором.

Denoify — это модуль для Node, denoify_ci — шаблонный репозиторий для настройки CI в проектах, использующих denoify.

Ручная поддержка несовместимых API


Костыли спасут мир. Скажем, если у вас используется вычисление sha256 из неподдерживаемого crypto, вы делаете следующую обёртку:

Для ноды подключаете hash.ts, где используется crypto:

import * as crypto from "crypto";

export function sha256(input: string): string {
  return crypto
	  .createHash("sha256")
	  .update(input)
	  .digest("hex");
}

Для Deno берёте только Sha256, который реализован отдельно:

import { Sha256 } from "https://deno.land/std@0.65.0/hash/sha256.ts";

export function sha256(input: string): string {
  return new Sha256()
    .update(input)
    .hex();
}

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

"dependencies": {
    "js-yaml": "^3.13.1",
    "ts-md5": "^1.2.7"
},
"denoify": {
    "ports": {
        "js-yaml": "https://deno.land/x/js_yaml_port/js-yaml.js",
        "ts-md5": "garronej/ts-md5"
    }
}

При этом, если в Node у вас такой импорт:

import * as Xxx from "xxx"

а в Deno такой:

import Xxx from "xxx"

то конвертация не сработает. Придётся либо искать переписанный исходник на pika или jspm, либо делать свой порт всего модуля. Затем нужно будет указать путь до порта в package.json:

"denoify": {
    "replacer": "dist/bin/customReplacer.js"
}

Затем нужно настроить tsconfig.json, который нужен Denoify для конвертации:

Укажите путь для сохранения созданных файлов Deno (по умолчанию dist/):

{
  "compilerOptions": {
    "outDir": "dist/"
  }
}

Исключите из компиляции файлы Deno:

{
  "exclude": [
    "node_modules",
    "dist/", // укажите свой путь
    "src/**/*.deno.ts",
    "src/**/*.deno.tsx",
  ]
}

Подправьте конфиг компилятора:

{
  "compilerOptions": {
    "noUnusedLocals": true, 
    "noUnusedParameters": true, 
    "strict": true 
  }
}

Теперь вы почти гарантированно получите тонну ошибок, но все они исправляются добавлением this, any или!..

Наконец, не забудьте установить Denoify и указать скрипты в package.json:

  "devDependencies": {
    "denoify": "^4.0.1",
  }
  "scripts": {
    "build": "tsc && denoify",
  }


И укажите файлы, которые должны быть скопированы в директорию с deno-файлами (по умолчанию это LICENSE и README.md):
"denoify": {
  "includes": [ ... ]
}

И всё! Теперь каждый раз при выполнении npm run build скомпилированные файлы Deno будут обновлены.

Публикация на deno.land


На deno.land/x нажмите Add a module и следуйте инструкциям. После публикации импортируйте модуль по ссылке, например:

import { Cat } from "https://deno.land/x/my_dummy_npm_and_deno_module@v0.4.2/mod.ts";

Заключение


Полноценное использование любых модулей Node в Deno всё ещё требует больших усилий, но в принципе возможно. Многие модули не используют неподдерживаемые API ноды, а значит доступны из коробки, а остальные можно либо найти на deno.land, pika и jspm, либо сконвертировать самостоятельно с помощью Denoify.
Всем разработчикам на Deno — удачи и терпения.



На правах рекламы


Эпичные серверы для разработчиков и не только! Недорогие VDS на базе новейших процессоров AMD EPYC и NVMe хранилища для размещения проектов любой сложности, от корпоративных сетей и игровых проектов до лендингов и VPN.

Комментарии (9)