Совместимость с npm пакетами

Fresh построен поверх Deno и изначально использует ES-модули, URL-импорты и строгую модель безопасности. При этом одной из ключевых особенностей фреймворка является возможность работать с экосистемой npm, несмотря на фундаментальные различия между Deno и Node.js. Совместимость достигается не за счёт эмуляции среды Node.js, а через встроенный механизм поддержки npm-пакетов в Deno, который Fresh использует напрямую.

В основе лежит несколько принципов:

  • ESM как основной формат — CommonJS пакеты транслируются.
  • Изоляция зависимостей — npm-модули устанавливаются и кэшируются Deno.
  • Отсутствие node_modules — управление зависимостями происходит централизованно.
  • Явное разрешение системных API — доступ к файловой системе, сети и процессам контролируется.

Механизм подключения npm-пакетов

Fresh не имеет собственного менеджера пакетов. Используется стандартный механизм Deno с префиксом npm:. Импорт npm-пакета выглядит как обычный ES-импорт:

import lodash from "npm:lodash";

При первом запуске Deno:

  • загружает пакет из npm-реестра,
  • анализирует зависимости,
  • преобразует CommonJS в ES-модули (если требуется),
  • кэширует результат.

Версия может быть зафиксирована напрямую в импорте:

import express from "npm:express@4.18.2";

Для Fresh это полностью прозрачный процесс — код фреймворка не различает npm-пакеты и модули, загруженные по URL.


Управление версиями и deno.json

Для крупных проектов прямые импорты с версиями становятся неудобными. Fresh поддерживает централизованное управление зависимостями через deno.json:

{
  "imports": {
    "lodash": "npm:lodash@4.17.21",
    "zod": "npm:zod@3.22.4"
  }
}

После этого используется короткий синтаксис:

import { z } from "zod";

Преимущества такого подхода:

  • единый источник правды для версий,
  • упрощение рефакторинга,
  • отсутствие «магии» автогенерации lock-файлов.

Deno самостоятельно формирует внутренний lock-механизм, обеспечивая воспроизводимость сборки.


CommonJS и трансформация модулей

Большая часть npm-экосистемы исторически использует CommonJS. Deno выполняет автоматическую трансформацию:

  • requireimport
  • module.exportsdefault export
  • именованные экспорты эмулируются

Пример:

import chalk from "npm:chalk";

Даже если chalk внутри использует CommonJS, в коде Fresh он выглядит как полноценный ES-модуль.

Ограничения:

  • динамический require может не работать,
  • модули, зависящие от __dirname, требуют адаптации,
  • использование process возможно только при разрешении соответствующих API.

Работа с Node.js API

Многие npm-пакеты используют стандартные модули Node.js (fs, path, crypto). Deno предоставляет совместимый слой:

import fs from "node:fs";
import path from "node:path";

Fresh позволяет использовать такие импорты без дополнительной настройки. Однако действуют правила безопасности Deno:

  • доступ к файловой системе требует --allow-read / --allow-write,
  • сетевые запросы — --allow-net,
  • переменные окружения — --allow-env.

Это означает, что npm-пакет может быть технически совместим, но функционально ограничен без соответствующих разрешений.


Пакеты для браузера и серверная изоляция

Fresh чётко разделяет серверный и клиентский код. npm-пакеты могут использоваться в обоих контекстах, но с разными требованиями.

Серверная часть:

  • допустимы пакеты с Node.js API,
  • возможна работа с БД, файловой системой, сетью,
  • код выполняется в Deno runtime.

Клиентская часть (islands):

  • только браузерные API,
  • отсутствие Node.js модулей,
  • предпочтение ESM-пакетам без side-effects.

Попытка использовать npm-пакет с fs внутри island приведёт к ошибке сборки или выполнения.


Типизация и TypeScript

Fresh использует TypeScript без этапа компиляции. npm-пакеты с типами подключаются автоматически:

  • встроенные .d.ts,
  • типы из @types/*,
  • декларации, встроенные в пакет.

Пример:

import axios from "npm:axios";

Типы axios будут доступны без дополнительной настройки. Если пакет не содержит типизации, возможны варианты:

  • ручное объявление типов,
  • использование // @ts-ignore,
  • замена пакета на типизированный аналог.

Deno не поддерживает автоматическую установку @types, поэтому предпочтение отдаётся современным пакетам с встроенными типами.


Ограничения и проблемные категории пакетов

Не все npm-пакеты одинаково совместимы с Fresh. Наиболее проблемные категории:

  • CLI-утилиты — завязаны на process.argv, stdin/stdout.
  • Native-модули — используют node-gyp, бинарные зависимости.
  • Пакеты с пост-инсталляционными скриптами — Deno их игнорирует.
  • Старые библиотеки — жёсткая привязка к Node.js окружению.

Для таких случаев используются альтернативы:

  • чистые ESM-реализации,
  • библиотеки, ориентированные на Web API,
  • собственные адаптеры.

Оптимизация бандла и tree shaking

Fresh не бандлит серверный код, но клиентские islands проходят оптимизацию. npm-пакеты, используемые в islands:

  • анализируются на предмет ESM-совместимости,
  • подлежат tree shaking,
  • включаются только при необходимости.

Лучше всего работают пакеты:

  • без побочных эффектов при импорте,
  • с модульной структурой,
  • без глобальных полифиллов.

Пример предпочтительного импорта:

import debounce from "npm:lodash.debounce";

Вместо:

import _ from "npm:lodash";

Стратегии выбора npm-пакетов для Fresh

При интеграции npm-зависимостей в проект на Fresh применяются практические критерии:

  • наличие ESM-сборки,
  • отсутствие native-зависимостей,
  • поддержка браузера (если используется в islands),
  • активное сопровождение.

Часто предпочтение отдаётся пакетам, ориентированным на web-стандарты, а не на Node.js как платформу.


Совместное использование URL-модулей и npm

Fresh допускает одновременное использование npm-пакетов и модулей по URL:

import { serve } from "https://deno.land/std/http/server.ts";
import z from "zod";

Это позволяет:

  • выбирать оптимальный источник зависимости,
  • избегать npm там, где есть стандартные Deno-модули,
  • снижать объём стороннего кода.

Такой гибридный подход является характерной особенностью экосистемы Fresh и Deno.


Поведение в продакшене и деплое

При деплое приложения на Fresh:

  • npm-пакеты уже кэшированы,
  • отсутствует шаг npm install,
  • сборка воспроизводима.

На платформах, поддерживающих Deno (включая Deno Deploy), npm-совместимость работает без дополнительной конфигурации, при условии отсутствия native-модулей.

Это делает Fresh особенно подходящим для serverless-архитектур и edge-окружений, где классический Node.js-подход с node_modules затруднителен.