Релиз Astro 7: переход на Rust, улучшенное кэширование и поддержка AI-разработки
- четверг, 25 июня 2026 г. в 00:00:10

Astro — фреймворк для сайтов, который минимизирует поставку JavaScript на клиент, обеспечивая высокую производительность. 22 июня вышла седьмая версия, в которой разработчики серьёзно прокачали скорость. Компилятор .astro переписали на Rust, туда же перенесли обработку Markdown и MDX, а движок рендеринга заменили на систему с очередями. Вкупе с Vite 8 и новым бандлером Rolldown сборки ускорились на 15–61% по внутренним бенчмаркам. А поскольку самая быстрая сборка — та, которую не нужно запускать вовсе, в Astro 7 также стабилизировали кэширование маршрутов и добавили экспериментальных CDN-провайдеров кэша для Netlify, Vercel и Cloudflare.
В Astro 7 добавили продвинутый роутинг: появляется точка входа src/fetch.ts, дающая полный контроль над конвейером обработки запросов в Astro. Для разработки с участием ИИ Astro теперь умеет определять coding agents, запускать dev-сервер в фоне и выводить структурированные JSON-логи, когда агентам нужен машиночитаемый ответ.
Чтобы обновить существующий проект до Astro 7, рекомендуется использовать автоматический CLI-инструмент @astrojs/upgrade:
# Рекомендованное: npx @astrojs/upgrade # Ручная установка: npm install astro@latest
Для нового проекта используйте команду:
npm create astro@latest
Подробности по миграции — в upgrade guide.
Astro 7 перешёл на Vite 8. Это обновление стало одним из крупнейших релизов Vite за последние годы. Главное изменение: Vite теперь поставляется с Rolldown, бандлером на Rust, который заменяет и esbuild, и Rollup единым стеком. В бенчмарках Rolldown показывает производительность в 10–30 раз выше, чем Rollup, при этом поддерживает тот же API плагинов Rollup и Vite.
Для пользователей Astro это означает более быстрые сборки без изменений конфигурации в большинстве проектов. В Vite 8 есть слой совместимости, который автоматически преобразует существующие настройки esbuild и rollupOptions в эквиваленты для Rolldown. Если проект использует кастомные плагины Vite, большая их часть должна продолжить работать, потому что Rolldown поддерживает тот же plugin API, что и Rollup.
Astro 7 — самая быстрая версия фреймворка на текущий момент. По мере роста экосистемы все больше команд проверяют, насколько далеко можно зайти со сайтами на Astro. После выхода прошлой версии и крупного внутреннего рефакторинга фокус сместился на масштабирование для более масштабных и сложных проектов.
Сборка устроена так:
Страницы сайта, контент и клиентские компоненты собираются в JavaScript.
Затем этот код запускается как небольшой сервер: для каждой prerendered-страницы создается запрос, а полученный HTML сохраняется.
Astro 7 ускоряет оба этапа, но основной выигрыш дает первый — бандлинг сайта. Самые заметные улучшения появились благодаря переносу части самых медленных участков сборки в нативный код на Rust. Этап генерации тоже стал быстрее за счет новой стратегии рендеринга, которая эффективнее работает с очередями задач.
Во внутренних тестах общее время сборки сократилось на 15–61%, а некоторые сайты стали собираться более чем в 2 раза быстрее. Наибольшую выгоду получили проекты, где существенную долю времени занимают компиляция .astro и обработка Markdown, потому что именно эти части были перенесены на Rust.
Бенчмарки запускались на MacBook Pro с Apple M4 Pro и 48 ГБ памяти:
https://docs.astro.build (~6 313 страниц): 114.54s → 73.53s
https://astro.build (~308 страниц): 62.70s → 24.24s
https://biomejs.dev (~6 488 страниц): 176.39s → 149.90s
https://developers.cloudflare.com (8 431 страниц): 386.89s → 261.94s
https://tauri.app (7 117 страниц): 86.12s → 55.33s
https://aspire.dev (13,275 pages) 385.84s 326.11s
Команда Astro создала новый компилятор для формата компонентов .astro, теперь написанный на Rust. Это полная переработка предыдущего компилятора на Go. Он в основном обратно совместим, но есть несколько отличий:
Больше нет автоматической коррекции HTML. Компилятор на Go молча переписывал разметку в «валидный HTML»: переставлял элементы, автоматически закрывал теги и перемещал узлы, что нередко удивляло пользователей и приводило к трудноуловимым ошибкам. Новый компилятор обрабатывает разметку как есть.
Строгость в стиле JSX. Незакрытые теги вроде <div>Hello и незавершенные атрибуты вроде <div class="Hello > теперь вызывают ошибки вместо молчаливой коррекции. Это реальные дефекты шаблонов, которые старый компилятор просто игнорировал, пытаясь вести себя как браузер.
Обработка пробелов как в JSX. Пробелы между элементами теперь схлопываются по правилам JSX, как в React и других JSX-фреймворках. Например, перенос строки между inline-элементами больше не создает видимый пробел:
<!-- До: рендерилось как "Hello World" (с пробелом) --> <!-- После: рендерится как "HelloWorld" (без пробела) --> <span>Hello</span> <span>World</span> <!-- Чтобы сохранить пробел, нужно явное выражение: --> <span>Hello</span>{' '}<span>World</span>
Переход на Rust позволил поставлять нативные бинарники для поддерживаемых платформ с WASM-фолбэком для сред, где это требуется. Такой подход уже стал стандартом в мире JavaScript-инструментов и используется, например, в Rolldown и Lightning CSS. Внутри новый компилятор опирается на oxc для парсинга и Lightning CSS для CSS scoping.
Изолированно компилятор на Rust дал примерно 6% ускорения сборки для https://docs.astro.build. Причина в том, что компиляция .astro редко бывает основным узким местом: обычно больше времени съедают Markdown и бандлинг. Но на больших сайтах с тысячами страниц даже такой прирост важен, а его эффект складывается с остальными оптимизациями релиза.
Astro 7 заменяет стандартный пайплайн Markdown и MDX на Sätteri — процессор на Rust, созданный jодной из core-команды Astro. Перевод сборок документации Astro и Cloudflare на Sätteri сократил время их сборки более чем на минуту, поэтому проекты с большим количеством Markdown-контента получили максимальный выигрыш в Astro 7.
До этого Markdown-пайплайн Astro работал на unified (remark, rehype и длинной цепочке JavaScript-зависимостей). На крупных сайтах с тысячами страниц именно этот этап часто был самым медленным: каждый файл проходил парсинг в JavaScript, затем обработку плагин за плагином по полному AST, а потом сериализацию обратно в HTML. В Astro 6.4 пайплайн сделали расширяемым и добавили Sätteri как опциональную альтернативу. В Astro 7 он становится стандартным вариантом.
В основе Sätteri лежат pulldown-cmark для CommonMark и Oxc для разбора выражений MDX — оба компонента написаны на Rust. Поставляются платформенные бинарники плюс WASM-фолбэк, по той же схеме, что и у нового .astro-компилятора. Но дело не только в скорости. Sätteri также нативно реализует множество Markdown-возможностей, которые раньше требовали отдельных плагинов:
GFM (таблицы, сноски, зачеркивание, task lists): в unified нужен remark-gfm.
Smart punctuation (типографские кавычки, длинные тире): в unified нужен remark-smartypants.
Heading IDs: в unified нужен remark-heading-id или аналог.
Container directives: в unified нужен remark-directive.
Math: в unified нужен remark-math.
Frontmatter (YAML, TOML): в unified нужен remark-frontmatter.
Superscript / subscript: в unified нужен remark-supersub или аналог.
Wikilinks: в unified нужен remark-wiki-link.
Функции, не включенные по умолчанию, активируются через features:
// astro.config.mjs import { defineConfig } from 'astro/config'; import { satteri } from '@astrojs/markdown-satteri'; export default defineConfig({ markdown: { processor: satteri({ features: { directive: true, math: true, headingAttributes: true, }, }), }, });
У Sätteri есть и собственный API плагинов. Плагины объявляют, какие типы узлов им интересны, и пропускают все остальное, вместо того чтобы каждый раз обходить все дерево целиком. Поэтому подключение плагинов здесь обходится заметно дешевле, чем в unified, где каждый плагин проходит по каждому узлу.
Если проект зависит от плагинов remark или rehype, пайплайн на базе unified по-прежнему доступен через @astrojs/markdown-remark:
// astro.config.mjs import { defineConfig } from 'astro/config'; import { unified } from '@astrojs/markdown-remark'; import remarkToc from 'remark-toc'; export default defineConfig({ markdown: { processor: unified({ remarkPlugins: [remarkToc], }), }, });
Подробнее о возможностях Sätteri и его plugin API — на satteri.bruits.org.
Queued rendering появился в прошлой версии как экспериментальная возможность, а теперь стал стабильным и используется по умолчанию. По данным команды, этот механизм дает ускорение примерно до 2,4 раза.
// astro.config.mjs import { defineConfig } from "astro/config"; export default defineConfig({ experimental: { queuedRendering: { enabled: true, pooling: true, contentCache: 1000 } } })
Раньше Astro рендерил страницы рекурсивно, когда дочерние узлы обрабатывались тем же render*-вызовом:
// render.ts export function renderComponentToString(node: unknown): string { let destination = ""; destination += <${node.name}>; // открытие тега // render attributes for (const child of node.children) { // Рекурсивный вызов для дочерних узлов destination += renderComponentToString(child); } destination += </${node.name}>; // closing tag return destination; }
Новый движок использует очередь (или стек) и один цикл. Очередь заполняется дочерними узлами в правильном порядке, а цикл продолжает рендеринг, пока очередь не опустеет:
// render.ts export function renderComponentToString(root: unknown): string { let destination = ""; destination += <${root.name}>; // открытие тега // очередь, которая пополняется и опустошается в процессе рендеринга let stack = [root]; while (stack.length > 0) { const node = stack.pop(); if (Array.isArray(node)) { // Узлы в конце должны попасть в destination первыми for (let i = node.length - 1; i >= 0; i--) stack.push(node[i]); continue; } const nodeType = typeof node; if (nodeType === 'string') { destination += escapeHTML(node as string); } } destination += </${root.name}>; // closing tag return destination; }
Первая реализация этого подхода состояла из двух фаз: сначала создавался упорядоченный список компонентов, затем список проходился и рендерился. Финальная реализация больше не создает полный список заранее: элементы рендерятся прямо во время обхода. Такой вариант быстрее первой итерации и требует меньше памяти, чем рекурсивный подход.
Astro начинался как генератор статических сайтов с файловой маршрутизацией. Со временем middleware, redirects, rewrites, Actions, sessions и i18n заметно расширили серверные возможности Astro-приложений, но при этом усложнили контроль над жизненным циклом запроса. Если, например, нужно было запускать auth раньше Actions, логировать только рендеринг страниц или сначала отдавать часть запросов не-Astro API, приходилось обходить существующий пайплайн, а не собирать его напрямую из нужных частей.
В Astro 7 можно полностью перехватить управление конвейером обработки запросов, добавив в проект файл src/fetch.ts. Этот файл экспортирует стандартный паттерн fetch-обработчика, знакомый по Cloudflare Workers, Deno и Bun.
// src/fetch.ts import { astro, FetchState } from 'astro/fetch'; export default { fetch(request: Request) { const state = new FetchState(request); // Проксирование API-запросов в backend-сервис if (state.url.pathname.startsWith('/api')) { const url = new URL(state.url.pathname + state.url.search, 'https://backend-api.example.com'); return fetch(new Request(url, request)); } // Фолбэк на страницы и endpoints Astro return astro(state); } }
API совместим и с Hono, поэтому middleware Hono можно напрямую использовать внутри Astro-приложения:
// src/fetch.ts import { astro } from 'astro/hono'; import { Hono } from 'hono'; import { basicAuth } from 'hono/basic-auth'; const app = new Hono(); app.use(basicAuth({ username: 'admin', password: 'secret' })); app.use(astro()); export default app;
Для продвинутых сценариев отдельные возможности Astro можно собирать как независимые middleware, полностью контролируя порядок обработки запроса:
// src/fetch.ts import { Hono } from 'hono'; import { actions, middleware, pages, i18n } from 'astro/hono'; import { auth } from './middleware/auth'; import { timing } from './middleware/timing'; const app = new Hono(); app.use(i18n()); app.use(auth()); // auth выполняется до actions app.use(actions()); app.use(middleware()); app.use(timing()); // timing оборачивает только рендеринг страниц app.use(pages()); export default app;
Если файл src/fetch.ts не добавлен, Astro работает точно так же, как и раньше.
Кэшировать on-demand-рендеринг сложнее, чем хотелось бы: у каждого хостинга свои правила, а единый стандарт управления из кода приложения долгое время отсутствовал. В Astro 7 для этого появляется route caching. Впервые функция появилась как эксперимент в прошлой версии, а теперь стабилизирована и дает единую платформенно-независимую модель: директивы задаются в маршрутах, а Astro сам делает все необходимое на выбранной платформе.
Провайдер кэша настраивается один раз, после чего можно использовать Astro.cache в страницах или context.cache в API-маршрутах и middleware, управляя кэшированием каждого ответа в терминах стандартной HTTP-семантики. В Astro есть встроенный провайдер memoryCache() для быстрого старта:
// astro.config.mjs import { defineConfig, memoryCache } from 'astro/config'; export default defineConfig({ cache: { provider: memoryCache(), }, }); --- Astro.cache.set({ maxAge: 120, // кэш на 2 минуты swr: 60, // еще 1 минуту можно отдавать устаревшее значение во время ревалидации tags: ['products'], // тег для точечной инвалидции }); ---
Можно также декларативно задавать правила кэширования для групп маршрутов через routeRules, не добавляя логику кэша в код самих страниц:
// astro.config.mjs export default defineConfig({ cache: { provider: memoryCache() }, routeRules: { '/blog/[...path]': { maxAge: 300, swr: 60 }, }, });
Особенно полезно это в связке с live content collections. Live loader может вернуть вместе с данными cache hint — с тегами для инвалидации и временем последнего изменения. Если передать такой entry напрямую в Astro.cache.set(), Astro сам прочитает эти подсказки:
import { getLiveEntry } from 'astro:content'; const { entry } = await getLiveEntry('products', Astro.params.id); // Astro прочитает cache hint из entry Astro.cache.set(entry);
Кэшированные ответы можно очищать по требованию через cache.invalidate() — по тегу или по пути. Например, можно поднять webhook endpoint, который CMS будет дергать при изменении контента. Тогда провайдер выполнит точечную очистку без полного rebuild:
// src/pages/api/revalidate.ts import type { APIRoute } from 'astro'; export const POST: APIRoute = async ({ request, cache }) => { // В реальном проекте здесь нужно проверить запрос и секретный токен. const { slug } = await request.json(); // Очистить все ответы с тегом 'products'... await cache.invalidate({ tags: ["products"] }); // ...очистить страницы, использовавшие конкретную запись... await cache.invalidate({ tags: [products:${slug}] }); // ...или удалить конкретный путь. await cache.invalidate({ path: /products/${slug} }); return new Response('Revalidated'); };
Если route caching уже использовался в экспериментальном режиме, единственное изменение — cache и routeRules теперь нужно вынести из блока experimental на верхний уровень конфигурации. В остальном API не изменился.
Когда route caching появился в прошлой версии, для Node adapter был доступен только один in-memory-провайдер. В Astro 7 добавлены экспериментальные CDN-провайдеры для Netlify, Vercel и Cloudflare (последний — в закрытой бете).
Вместо хранения ответов в памяти эти провайдеры передают директивы кэширования на edge-сеть хостинга, что дает еще более быстрые ответы. При cache hit CDN может вернуть ответ напрямую, вообще не вызывая серверную функцию.
В одном из следующих релизов эти провайдеры планируется включать автоматически, а пока в экспериментальной фазе их нужно подключать вручную. Достаточно импортировать провайдер для используемого адаптера и задать его как cache.provider:
// astro.config.mjs import { defineConfig } from 'astro/config'; import netlify from '@astrojs/netlify'; import { cacheNetlify } from '@astrojs/netlify/cache'; export default defineConfig({ adapter: netlify(), cache: { provider: cacheNetlify(), }, });
Каждый адаптер экспортирует провайдер из своего entrypoint /cache:
Netlify: @astrojs/netlify/cache → cacheNetlify()
Vercel: @astrojs/vercel/cache → cacheVercel()
Cloudflare: @astrojs/cloudflare/cache → cacheCloudflare()
Один и тот же API Astro.cache, routeRulesи cache.invalidate() работает со всеми провайдерами. Каждый из них переводит директивы в нативные cache-control-заголовки платформы и механизмы очистки по тегам или путям.
Важно: провайдер Cloudflare требует функции Cloudflare Workers Cache, которая сейчас находится в закрытой бете. Без доступа к бете этот провайдер не работает и использовать его не следует.
ИИ-агенты для написания кода уже стали частью рабочего процесса многих разработчиков, и dev-сервер им нужен в другом виде, чем людям. Astro 7 — первый шаг к тому, чтобы Astro лучше подходил для для разработки под управлением агентов.
ИИ-агентам сложно работать с долгоживущими процессами. Обычно агент запускает команду, ждет завершения и читает вывод, но dev-сервер не завершается сам по себе. В итоге агенты зависают, поднимают дублирующиеся серверы, теряют информацию об уже запущенных экземплярах или оставляют зомби-процессы. По наблюдениям команды Astro, это видно и в автоматизированном triage issue: иногда агент тратит больше времени на возню с dev-сервером, чем на реальную проверку кода.
В Astro 7 появляется astro dev --background, который запускает dev-сервер как управляемый фоновый процесс. Astro также умеет определять, что работает внутри AI-агента, и автоматически включает background mode — без дополнительных флагов в агентных сценариях. Если агент не обнаружен, astro dev ведет себя как раньше.
$ astro dev --background Dev server running at http://localhost:4321 (pid 12345) Stop: astro dev stop Status: astro dev status Logs: astro dev logs
Команда блокируется только до момента готовности сервера принимать запросы, после чего сообщает URL и PID и отсоединяется. Не нужны polling, sleep и парсинг терминального вывода в поисках строки Local:.
Lockfile защищает от запуска дубликатов. Если агент попытается поднять второй сервер, Astro просто вернет данные уже работающего экземпляра:
$ astro dev --background Dev server already running at http://localhost:4321 (pid 12345)
Проверять статус и останавливать сервер можно из других shell-сессий:
$ astro dev status Dev server running at http://localhost:4321 (pid 12345, uptime 123s, background) $ astro dev stop Stopped dev server (pid 12345).
Логи фонового сервера читаются через astro dev logs.
Все команды сделаны идемпотентными и «мягкими». Остановка несуществующего сервера проходит без ошибки, а повторный запуск возвращает уже существующий экземпляр. Это важно, потому что агенты часто теряют из виду текущее состояние процессов.
Кроме того, все работающие dev-серверы теперь публикуют health endpoint /_astro/status, который агент может опрашивать, чтобы убедиться, что сервер жив и готов принимать запросы.
Логгер Astro теперь полностью настраиваемый. Для агентов JSON-логирование включается автоматически, когда детектор агента активирует background mode. Для остальных пользователей эта возможность доступна через CLI или конфигурацию:
astro dev --json
// astro.config.mjs import { defineConfig, logHandlers } from "astro/config"; export default defineConfig({ logger: logHandlers.json() })
JSON-логирование было самым популярным запросом в roadmap Astro, и не только из-за AI. Командам, которые разворачивают Astro SSR в production, нужны структурированные логи для интеграции с системами агрегации вроде Kibana, CloudWatch и Grafana/Loki. Прежний формат логов Astro был жестко ориентирован на человека: цвета, псевдографика, многострочные ошибки. Для машин такой вывод неудобен.
Новый API логгера также поддерживает кастомные обработчики и compose() для объединения нескольких логгеров. Например, можно оставить человекочитаемый вывод в консоли и параллельно писать JSON для инструментов, которым нужна структурированная телеметрия:
// astro.config.mjs import { defineConfig, logHandlers } from "astro/config"; export default defineConfig({ logger: logHandlers.compose( logHandlers.console(), logHandlers.json() ) })
Подробнее о поддержке AI-инструментов — в AI guide.
НЛО прилетело и оставило здесь промокод для читателей нашего блога:
-15% на заказ нового VDS — HABRFIRSTVDS.