Dependency scanning во фреймворке Fresh — это механизм статического анализа зависимостей, который используется для автоматического определения клиентских и серверных модулей, а также для точного формирования графа импортов. В отличие от классических SPA-фреймворков, Fresh изначально проектировался с упором на минимальный JavaScript на клиенте и строгую изоляцию серверного и клиентского кода. Dependency scanning является ключевым элементом этой архитектуры.
Fresh работает поверх Deno, используя его модульную систему и стандартные ES-модули. Сканирование зависимостей выполняется во время сборки и в режиме разработки, без необходимости явной конфигурации со стороны разработчика.
Fresh придерживается нескольких фундаментальных принципов:
Dependency scanning обеспечивает реализацию этих принципов за счёт анализа импортов, сигнатур компонентов и контекста их использования.
Сканирование зависимостей во Fresh опирается на несколько типов входных данных:
Fresh использует строгую конвенцию каталогов:
routes/ — серверные маршрутыislands/ — интерактивные клиентские компонентыcomponents/ — серверные или универсальные
компонентыstatic/ — статические ресурсыУже на этом уровне dependency scanner делает первичную классификацию модулей.
Для каждого TypeScript/JavaScript-файла строится абстрактное синтаксическое дерево. Анализируются:
import и exportAST-анализ позволяет определить фактические зависимости, а не просто текстовые импорты.
Ключевая задача dependency scanning — определить, какие зависимости должны попасть в клиентский бандл, а какие остаются на сервере.
К серверным относятся:
routes/Эти зависимости никогда не включаются в клиентский JavaScript.
Клиентские зависимости формируются исключительно из island-компонентов:
islands/Dependency scanner строит отдельный граф зависимостей для каждого island.
Island-компоненты являются точкой входа для клиентского кода. Dependency scanning выполняет следующие шаги:
islands/Каждый island изолируется: его зависимости не смешиваются с другими islands, даже если используются одинаковые модули.
Fresh не объединяет client bundles автоматически. Dependency scanning намеренно сохраняет изоляцию, но при этом выполняет анализ на уровне модулей:
Сканирование транзитивных зависимостей выполняется до тех пор, пока не будет достигнут листовой модуль без импортов.
Dependency scanning во Fresh поддерживает динамические импорты
(import()), но с ограничениями:
Это позволяет избежать непредсказуемого поведения и сохранять детерминированность dependency graph.
Во Fresh наличие JSX не делает модуль автоматически клиентским. Dependency scanning учитывает контекст:
routes/ — серверный рендерингcomponents/ — серверный, если не используется в
islandislands/ — клиентскийТаким образом, среда выполнения определяется не синтаксисом, а путём распространения зависимости.
В режиме разработки dependency scanning тесно интегрирован с hot module replacement:
Это достигается за счёт точного знания, какие модули связаны между собой.
Dependency scanning даёт несколько побочных эффектов:
Fresh избегает эвристик и магии, опираясь исключительно на анализ зависимостей.
Dependency scanning во Fresh не пытается решить все возможные случаи:
Эти ограничения упрощают модель и делают поведение фреймворка прозрачным.
Внутри Fresh dependency graph представлен ориентированным ациклическим графом:
Каждый граф аннотирован метаданными:
Это представление используется как для dev-сервера, так и для production-режима.
Во Fresh dependency scanning — не вспомогательная оптимизация, а базовый архитектурный механизм. Он определяет, как код исполняется, где он исполняется и в каком объёме доставляется пользователю. Благодаря строгому анализу зависимостей фреймворк достигает предсказуемости, простоты и высокой производительности без сложных конфигураций и скрытых этапов сборки.