Shiki: подсветка кода — мастерская и 7 примеров для документации
Делаем примеры кода удобными: пять языков, две темы, выделение строк, авторские пометки, копирование и статический HTML. Семь сценариев, мастерская и исходники.

Shiki на практике: читаемая документация, две темы, выделение строк, авторские пометки, копирование и HTML без JavaScript. Семь сценариев, мини-мастерская и исходники Windows/Linux.
Shiki: превратить код в понятный элемент интерфейса
На странице документации код часто оказывается самым важным содержимым. Посетитель приходит за запросом к API, конфигурацией Nginx, примером PHP или способом исправить ошибку. Если фрагмент трудно читать, неудобно копировать и невозможно рассмотреть на телефоне, подробный текст вокруг помогает лишь частично. Хорошо оформленный пример ускоряет переход от чтения к собственному результату.
Shiki превращает исходный текст в HTML с подсветкой синтаксиса. Библиотеку можно запускать в браузере, на Node.js или во время сборки сайта. В нашем проекте она работает двумя способами: первые шесть сценариев обрабатывают текст прямо на странице, седьмой показывает настоящие HTML-файлы, заранее созданные Node.js. Это позволяет сравнить интерактивный инструмент и готовую документацию без браузерного highlighter.
После статьи вы сможете встроить читаемый пример в справку своего сервиса, добавить две темы, номера и выделение строк, показать изменение настройки, сохранить исходник и подготовить статическую страницу для PHP-сайта. В мастерской всё можно попробовать: редактируйте текст, меняйте оформление, скачивайте результат. Полный проект содержит исходники, тесты и команды для Windows и Linux.
Где это полезно: API, инструкции, обучение и внутренняя база знаний
Первый сценарий — документация API. Один пользователь хочет увидеть fetch в JavaScript, другой работает с PHP, третий проверяет JSON ответа. Переключение языка и отдельная кнопка копирования помогают быстро перенести пример в проект. При этом рядом с кодом всё равно нужны адрес, назначение полей, ожидаемый ответ и описание ошибок: цвета не заменяют документацию.
Второй сценарий — инструкция по эксплуатации. Администратору важно увидеть, какую строку конфигурации заменить, какое значение было раньше и почему выбрано новое. Номера строк, фон изменения и пояснение рядом уменьшают вероятность неверной правки. Такой блок пригодится в журнале релизов, руководстве по миграции или базе знаний технической поддержки.
Третий сценарий — обучение. Автор может выделить проверку ответа, показать предупреждение или разобрать фрагмент большого файла, сохранив исходную нумерацию. Читатель сначала понимает небольшой участок, затем скачивает весь пример. Для пользовательского редактора, где нужны автодополнение, несколько курсоров и диагностика при вводе, понадобится отдельный редактор; Shiki отвечает за представление текста.
Как работает подсветка и чего она не проверяет
Shiki использует грамматику выбранного языка и тему оформления. Грамматика помогает разбить текст на токены, тема назначает им цвета. Результат codeToHtml — строка HTML с блоком pre, code и вложенными span. Этот результат можно сохранить в файл, вставить в шаблон или показать в контейнере страницы. Основные варианты подключения описаны в официальном руководстве Shiki.
Подсветка не запускает код. Пример fetch не обращается к API, PHP не выполняется, а Nginx не перечитывает конфигурацию. Даже если ошибочный JavaScript выглядит правдоподобно, это не означает, что он запустится. Библиотека не заменяет линтер, проверку схемы, тесты или команду nginx -t. Для учебной страницы это особенно важно: пользователь должен понимать, что именно произошло после нажатия кнопки.
В мастерской исходник остаётся текстом, а обработка запускается через настоящий Shiki. Комментарии с пометками ошибок и изменений расставлены автором. Эти акценты помогают объяснять материал, но не являются результатом автоматического анализа. Вставленный фрагмент HTML также показывается как код, а не как действующая страница внутри результата.
Версии, состав проекта и два способа доставки результата
В архиве зафиксированы Shiki 4.6.0, @shikijs/transformers 4.6.0 и Bootstrap 5.3.9. Сборка использует esbuild 0.28.2. Для повторения выбран Node.js 24 LTS не ниже 24.15; зависимости закреплены package-lock.json. Версии сверены 13 ноября 2026 года. Обновление библиотеки лучше проводить отдельным изменением вместе с повтором тестов и просмотром примеров.
Мы включили пять грамматик: JavaScript, PHP, JSON, HTML и Nginx. Темы две: github-light и github-dark. Это осознанный набор для прикладной документации, а не весь каталог языков Shiki. В проекте используется JavaScript regex engine. Он работает в современных средах с необходимыми возможностями RegExp; совместимость старых браузеров нужно проверять отдельно.
Интерактивная мастерская загружает библиотеку и обрабатывает ввод на устройстве читателя. Статический вариант получает уже готовый HTML и CSS. Для обычной статьи, в которой текст не меняется после публикации, второй путь часто удобнее: посетителю не требуется загружать highlighter. Для конструктора примеров или инструмента технической поддержки полезен первый путь.
npm ci --ignore-scripts
npm test
npm run build
npm run serve
Мини-мастерская: семь задач и полезный маршрут по странице
Начните с выбора задачи. Под заголовком каждого примера указаны назначение, действие для проверки и объяснение результата. В первых шести задачах измените исходник и нажмите «Подсветить». После редактирования интерфейс сообщает, что результат устарел, и отключает выгрузку HTML до обновления. Так сохранённый файл соответствует тому, что вы действительно обработали.
Светлая и тёмная тема, номера и перенос строк меняют представление уже готового результата. Для них повторная токенизация не нужна. Можно скачать исходный текст отдельно или получить автономную HTML-страницу со стилями. В нижнем разделе доступны файлы проекта: выберите реализацию, прочитайте код и скачайте нужный файл.
Седьмой пример устроен иначе: он открывает один из трёх файлов настоящей сборки Node.js. Вы увидите размер и SHA-256 артефакта. Редактор в этом режиме скрыт, потому что изменение формы не пересобирает опубликованный файл. Мастерская обрабатывает только введённый текст, не исполняет его и не отправляет содержимое редактора на сервер.
Сценарий 1. Документация API на JavaScript, PHP и JSON
Выберите первый пример и переключите язык. Форма подставит соответствующий небольшой исходник: запрос API, ответ PHP, JSON, HTML-карточку или конфигурацию Nginx. Затем можно изменить текст и снова запустить подсветку. Указанный язык выбирает грамматику явно; автоматическое распознавание здесь не применяется. Для API-справки это полезно: автор точно знает, какой вариант он публикует.
Попробуйте поменять строку ошибки в JavaScript или добавить поле в JSON. Shiki выделит строки, ключевые слова и другие токены по грамматике. Ошибка структуры JSON при этом не обязана сопровождаться сообщением валидатора: библиотека оформляет текст. Если страница обещает проверку JSON, потребуется отдельный парсер с понятным выводом ошибок.
В своём сервисе держите данные примера отдельно от кнопок интерфейса. Один объект с языком, исходником и пояснением проще обновлять, переводить и тестировать. Не помещайте настоящие токены доступа в демонстрационный запрос: опубликованный исходник предназначен для чтения и скачивания. В архиве используются только учебные значения.
import { highlight } from './src/highlight.mjs';
const source = `const response = await fetch('/api/orders');
if (!response.ok) throw new Error('Ошибка загрузки');
const orders = await response.json();`;
const html = await highlight(source, { lang: 'javascript' });
// highlight — проверяемая обёртка нашего проекта над Shiki.
// html содержит оформление текста; запрос fetch не выполнялся.
Создать highlighter один раз и загрузить только нужные грамматики
Инициализация highlighter готовит грамматики, темы и движок. Не стоит создавать новый экземпляр для каждого небольшого блока на странице. В src/highlight.mjs мы сохраняем обещание инициализации в переменной: повторные вызовы используют один результат, в том числе если несколько фрагментов начали обрабатываться одновременно. Поведение закреплено отдельным тестом.
Вместо общего импорта всех возможностей используются core, конкретные грамматики и две темы. Точечный набор делает состав сборки понятным: если документация никогда не показывает другой язык, незачем включать его только на всякий случай. Официальное руководство по сборкам Shiki объясняет различия готовых и настраиваемых вариантов.
Ниже сокращённая инициализация с одной грамматикой. В архиве к ней добавлены остальные четыре. В длительно работающем приложении также нужно определить жизненный цикл экземпляра: общий highlighter обслуживает много запросов, а временные экземпляры должны освобождаться, когда больше не нужны. Рекомендации приведены в разделе производительности.
import { createHighlighterCore } from 'shiki/core';
import { createJavaScriptRegexEngine } from 'shiki/engine/javascript';
import javascript from '@shikijs/langs/javascript';
import light from '@shikijs/themes/github-light';
import dark from '@shikijs/themes/github-dark';
let ready;
export function getHighlighter() {
return ready ??= createHighlighterCore({
langs: [javascript], themes: [light, dark],
engine: createJavaScriptRegexEngine()
});
}
Сценарий 2. Светлая и тёмная справка без повторной обработки
Откройте вторую задачу и переключите тему. Исходник PHP остаётся тем же, меняются фон и цвета токенов. Обе палитры уже присутствуют в сгенерированном HTML: вариант для тёмной темы хранится в CSS-переменных. Класс контейнера определяет, какие значения применяются. Механизм подробно описан в руководстве по двум темам.
Это пригодится сайту с собственным переключателем оформления. Вы связываете класс блока кода с настройкой сайта, сохраняя одинаковый текст, номера и выбранные строки. Пользователь не ждёт новой подсветки только потому, что предпочитает тёмный фон. При предварительной сборке можно сразу сохранить оба набора цветов.
Проверяйте оформление в контексте всей страницы. Хорошо читаемые токены не спасут подписи, если текст кнопки или выделение строки сливаются с фоном. Для своего дизайна следует проверить обычные строки, выбранный диапазон, добавление, удаление, ошибку и предупреждение в обеих темах. В мастерской это можно сравнить на одних и тех же примерах.
const html = (await getHighlighter()).codeToHtml(source, {
lang: 'javascript',
themes: { light: 'github-light', dark: 'github-dark' },
defaultColor: 'light'
});
// CSS контейнера .dark использует --shiki-dark и --shiki-dark-bg.
// Переключение класса не вызывает codeToHtml заново.
Сценарий 3. Выделить строки, которые объясняет автор
В третьем примере подсвечена строка проверки ответа. Введите диапазон 1,3-4, затем нажмите «Подсветить». Выбранные строки получат фон. Попробуйте неверный диапазон: ноль, обратный интервал или строку за пределами фрагмента. Вместо случайного результата форма покажет сообщение и не позволит выгрузить устаревший HTML.
Выделение относится к порядку строк внутри исходника. Это отличается от отображаемой нумерации: если первая строка подписана числом 40, диапазон 2 выбирает вторую строку и на экране соответствует номеру 41. Такой контракт полезен в руководстве по большому файлу: можно показать исходные номера, сохранив простую настройку небольшого фрагмента.
Не выделяйте сразу весь блок: акцент перестанет помогать. Лучше разбить длинное объяснение на шаги, указав возле каждого фрагмента, почему важна конкретная проверка. В исходниках parseLines разбирает список, проверяет границы и убирает повторы. Это наша прикладная функция, а не отдельный валидатор, встроенный в интерфейс Shiki.
const html = await highlight('first\nsecond\nthird', {
lang: 'javascript',
start: 40,
lines: '2-3'
});
// На экране номера 40, 41, 42.
// Выделены второй и третий элементы: номера 41 и 42.
Transformer: добавлять оформление через структуру HTML
Transformer позволяет изменять результат на определённом этапе формирования разметки. Например, обработчик line получает узел строки и её порядковый номер. В проекте через него добавляются data-line для декоративной нумерации и класс selected-line для выбранного диапазона. Такой способ сохраняет работу на уровне структуры, вместо поиска фрагментов в готовой HTML-строке.
Для собственного сайта transformer может добавлять классы, необходимые существующей дизайн-системе, или связывать строку с пояснением. Но любые дополнительные атрибуты должны строиться из проверенных значений. В нашем случае номер — целое число в ограниченном диапазоне, а имена CSS-классов заданы в коде. Произвольный пользовательский HTML через этот механизм не принимается.
Полный контракт и доступные этапы описаны в документации transformers. После добавления своего обработчика повторите тест экранирования: расширение не должно превращать текст примера в активную разметку. И отдельно проверьте, не исчезли ли номера и выделение при сочетании нескольких transformers.
const selected = new Set([2]);
const start = 40;
const transformer = {
name: 'aen-lines',
line(node, line) {
node.properties['data-line'] = start + line - 1;
if (selected.has(line)) this.addClassToHast(node, 'selected-line');
}
};
// Передайте transformer в массив transformers при codeToHtml.
Сценарий 4. Показать изменение настройки
В четвёртой задаче одна строка помечена как удалённая, другая — как добавленная. Комментарии [!code –] и [!code ++] обрабатывает transformerNotationDiff. После обработки служебная пометка скрывается в оформленном блоке, а строка получает соответствующий класс. Цвет и знак помогают увидеть, какое значение меняется.
Это авторское объяснение изменения, а не вычисление разницы между двумя файлами. Если вам нужен настоящий diff, сначала сравните версии отдельным инструментом, затем представьте результат. В примере намеренно находятся две декларации одного имени: они показывают альтернативы до и после. Копировать весь блок как готовую исполняемую программу не следует.
Такой приём удобен в инструкции по обновлению тайм-аута, переключению адреса API или исправлению условия. Рядом напишите причину изменения и проверку результата. Само по себе увеличение тайм-аута не объясняет, какую проблему оно решает и какой новый предел ожидания получит пользователь. Хорошая публикация соединяет визуальную разницу с последствиями для приложения.
const timeout = 1000; // [!code --]
const timeout = 5000; // [!code ++]
const retries = 2;
// Учебное сравнение двух вариантов, а не готовый файл для запуска.
Сценарий 5. Предупреждение, ошибка и важная строка
Пятая задача показывает три вида авторских акцентов. Warning может обозначать участок, требующий внимания, error — объясняемую ошибку, highlight — строку, на которой строится вывод. В нашем примере используется учебная проверка доступа. Подсветка не определяет, безопасна ли эта логика: смысл каждого акцента задаёт автор материала.
Это полезно в разборе ревью, инструкции для поддержки или учебном объяснении исключения. Однако сообщение об ошибке должно быть доступно словами, а не только через красный цвет. Напишите, что именно неверно, при каком входе проявляется проблема и чем заменить участок. Иначе посетитель увидит оформление, но не получит способ исправления.
Включение нотаций управляет сразу несколькими transformers. Их синтаксис и варианты оформления приведены в документации @shikijs/transformers. При копировании из нашей мастерской пользователь получает исходный текст, включая комментарии-пометки. Это сохраняет возможность повторно подсветить пример, но требует учитывать комментарии при вставке в свой проект.
import {
transformerNotationDiff,
transformerNotationHighlight,
transformerNotationErrorLevel
} from '@shikijs/transformers';
const transformers = [
transformerNotationDiff({ matchAlgorithm: 'v3' }),
transformerNotationHighlight({ matchAlgorithm: 'v3' }),
transformerNotationErrorLevel({ matchAlgorithm: 'v3' })
];
Сценарий 6. Номера большого файла, перенос и чистое копирование
Шестая задача показывает фрагмент HTML, нумерация которого начинается с 40. Это помогает связать небольшой пример с большим исходным файлом. Номера выводятся декоративно через data-line и CSS, поэтому они не должны становиться частью скачиваемого исходника. В нашем интерфейсе копирование читает строку редактора, а не текст оформленного DOM-блока.
Включите перенос и уменьшите ширину окна. Длинная логическая строка может занять несколько экранных строк, но номер исходника остаётся один. Без переноса код прокручивается внутри своей области. На телефоне оба варианта имеют смысл: перенос удобен для чтения текста и HTML, горизонтальная прокрутка лучше сохраняет структуру некоторых конфигураций.
Clipboard зависит от безопасного контекста и разрешений браузера. Если копирование отклонено, интерфейс предлагает выделить текст или скачать файл. Пользователь не должен терять пример из-за ограничения буфера обмена. Поэтому отдельная кнопка выгрузки исходника полезна даже на сайте, где основным действием считается «Скопировать».
const source = checkSource(editor.value);
try {
await navigator.clipboard.writeText(source);
status.textContent = 'Исходный текст скопирован';
} catch {
status.textContent = 'Выделите текст или скачайте исходник';
}
// Не используем textContent оформленного блока с декоративными элементами.
Экспорт HTML: результат должен соответствовать текущему исходнику
Кнопка выгрузки HTML создаёт самостоятельную страницу с кодировкой UTF-8, viewport и CSS оформления. Открывать такой файл можно без Shiki: токены уже преобразованы в разметку. В экспорт попадают выбранная тема, номера и перенос. Это удобно для передачи небольшого примера коллеге или подготовки статического материала.
Есть важная разница между изменённым редактором и последним обработанным результатом. Если пользователь ввёл новый текст, а подсветка ещё не выполнена, сохранение старого HTML будет неожиданным. Поэтому мастерская помечает состояние как устаревшее и блокирует HTML-экспорт. Скачивание исходника остаётся самостоятельным действием и работает с текущим текстом редактора.
Экспортируемая страница не является средой исполнения. HTML-код внутри примера остаётся видимым текстом, скриптов для запуска исходника в файле нет. Если нужен интерактивный учебный стенд, его следует создавать отдельной системой с явно обозначенными возможностями. Здесь результат предназначен для чтения, публикации и сравнения оформления.
import { highlight, documentHtml } from './src/highlight.mjs';
import { writeFile } from 'node:fs/promises';
const rendered = await highlight('const answer = 42;', {
lang: 'javascript'
});
const page = documentHtml(rendered, {
dark: true, numbers: true, wrap: false
});
await writeFile('example.html', page, 'utf8');
Сценарий 7. Предварительная сборка: три страницы без JavaScript
Седьмой пример открывает настоящие артефакты команды Node.js: JavaScript, PHP и Nginx. Их содержимое получено тем же highlighter, которым пользуется мастерская. Рядом указаны версия Node, размер и SHA-256 файла. Это позволяет связать результат с конкретной сборкой и проверить, что при публикации загружен именно подготовленный файл.
На странице артефакта нет исполняемого JavaScript. Сервер отдаёт готовую разметку и стили, а браузер рисует токены обычными средствами HTML/CSS. Такой путь подходит документации, где примеры меняются только при обновлении материала. Обработка переносится со всех устройств читателей на один этап подготовки публикации.
Для изменения откройте snippets в src/cases.mjs и выполните команду ниже. Она обновит public/prebuilt и manifest.json. Редактор встроенной мастерской не меняет эти файлы: это другой поток данных. После изменения исходников полной мастерской дополнительно выполните npm run build, чтобы обновились её JavaScript, стили и набор показываемых исходников.
npm run prebuild:examples
# public/prebuilt/javascript.html
# public/prebuilt/php.html
# public/prebuilt/nginx.html
# public/prebuilt/manifest.json
# Пересобрать также сам интерфейс:
npm run build
Интеграция в PHP-сайт: Node нужен при подготовке, PHP отдаёт страницу
Для PHP-проекта необязательно запускать Node на каждом запросе посетителя. Подготовьте оформление в процессе сборки, сохраните доверенный HTML-фрагмент и подключите его в шаблон страницы. В архиве демонстрируются целые HTML-документы; для вставки внутрь существующей страницы используйте результат highlight и подключите подходящие стили отдельно.
Разделяйте исходник примера и готовую разметку. Исходник остаётся обычной строкой и хранится рядом с материалом. Результат сборки содержит служебные span и классы, поэтому повторное экранирование всего фрагмента покажет пользователю сами теги. Допускать такую разметку в шаблон можно только из собственного проверенного процесса подготовки, а не из произвольной внешней строки.
Не используйте PHP include для пользовательского HTML. Для своего статического артефакта достаточно чтения файла из заранее определённого пути. Если сайт построен на Twig, см. подробную статью о шаблонах Twig: границу доверия между данными и разрешённой разметкой нужно сохранять при любой системе шаблонов.
<?php
// Файл создан нашим этапом сборки, путь не берётся из запроса.
$path = __DIR__ . '/generated/order-example.html';
$fragment = file_get_contents($path);
if ($fragment === false) {
throw new RuntimeException('Не найден подготовленный пример');
}
echo $fragment;
// generated/order-example.html — фрагмент highlight(), не целая страница.
Markdown, CMS и оформление технической документации
В документации на Markdown код обычно хранится внутри fenced-блоков с указанным языком. На этапе обработки Markdown можно передать содержимое блока Shiki, затем включить полученный фрагмент в страницу. При таком подходе автор продолжает работать с простым исходным текстом, а оформление собирается автоматически вместе с документом.
В CMS полезно отделить поля «исходник», «язык» и «пояснение». Тогда изменение темы не требует редактировать каждую статью, а поиск и копирование могут использовать чистый текст. Если редактор CMS удаляет служебные атрибуты или стили, результат нужно проверить после сохранения: локально правильный HTML ещё не доказывает правильное отображение опубликованной страницы.
На сайте уже есть материалы про League CommonMark в PHP и документацию на Docus. Shiki решает более узкую задачу оформления кода. Интеграцию с конкретным Markdown-процессором нужно подключать и тестировать отдельно; архив этой статьи демонстрирует прямой вызов библиотеки.
Экранирование, пользовательский ввод и политика страницы
Посетитель может вставить в редактор строку с тегом script или обработчиком onerror. Для подсветки это обычный текст. Тест проекта проверяет, что такие фрагменты не превращаются в настоящие элементы script и img внутри результата. Ввод не передаётся в eval, Function или PHP-интерпретатор: библиотека создаёт представление текста.
При расширении проекта опасность часто появляется вокруг highlighter: в собственном transformer, подписи, имени файла или шаблоне. Не соединяйте непроверенный ввод с активной HTML-разметкой. Используйте textContent для обычных сообщений, допустимый список языков для выбора грамматики и проверку числовых параметров. Наш HTML-экспорт принимает фиксированные настройки оформления, а не произвольный шаблон от посетителя.
Также согласуйте стили с CSP сайта. В этой мастерской и автономном экспорте используется встроенное оформление, поэтому политика, полностью запрещающая соответствующие inline-стили, потребует иной схемы доставки CSS. Не ослабляйте политику целиком ради подсветки: выберите подходящий способ формирования классов и стилей, затем проверьте его на реальной странице.
// Такой ввод должен стать видимым текстом, а не активным HTML.
const input = '<img src=x onerror=alert(1)><script>alert(1)</script>';
const rendered = await highlight(input, { lang: 'html' });
// В tests/highlight.test.mjs проверяется отсутствие настоящих img/script.
// Дополнительные transformers требуют такого же отдельного теста.
Большие фрагменты, размер сборки и отзывчивость интерфейса
Учебная форма ограничена 12 000 символов и 200 строками. Это предел нашей мастерской, а не универсальный предел Shiki. Он делает опыт предсказуемым и помогает показать понятную ошибку вместо длительного ожидания. Для реального редактора больших файлов ограничения следует выбирать по измерениям на устройствах вашей аудитории.
Текущая браузерная обработка выполняется в основном потоке. Очень большой или сложный текст может мешать интерфейсу отвечать на действия. Если продукт должен обслуживать такие фрагменты, рассмотрите Web Worker, ограничение объёма, отмену устаревших запросов и отдельный контроль времени. Эти механизмы требуют самостоятельной реализации; наличие кнопки не означает, что работа уже вынесена в worker.
В проверенной сборке app.js занимает 797 842 байт, измеренный gzip — 164 233 байт. CSS вместе с Bootstrap — 233 957 байт, gzip — 31 986. Значения записаны в public/build-info.json и изменятся после правок зависимостей. Размер gzip в файле отчёта не доказывает, что сервер действительно отправляет сжатый ответ: это проверяется в Network браузера.
# После своей сборки смотрим фактический отчёт:
npm run build
node -e "console.log(JSON.stringify(JSON.parse(require('fs').readFileSync('public/build-info.json','utf8')).sizes,null,2))"
# При публикации отдельно проверяем Content-Encoding и переданный размер.
JavaScript engine и совместимость браузеров
В проекте выбран JavaScript regex engine, поэтому отдельный WebAssembly-файл Oniguruma для этой сборки не загружается. Выбор движка влияет на совместимость и упаковку проекта. Поддержка современных возможностей регулярных выражений требуется и среде сборки, и браузеру, если highlighter запускается на устройстве пользователя.
Для широкой аудитории со старыми браузерами есть два практических направления: заранее подготовить HTML или собрать вариант с другим движком, после чего проверить нужные грамматики и способы доставки ресурсов. Официальный раздел RegExp Engines описывает доступные варианты и различия. Переключение движка следует рассматривать как изменение сборки, а не как обещание мгновенной совместимости со всеми клиентами.
Статический экспорт убирает требование запускать Shiki в браузере, но обычная вёрстка всё равно нуждается в проверке. Проверьте ширину контейнера, перенос, контраст и поведение длинных строк. Для документации нередко разумно использовать предварительную сборку по умолчанию, а интерактивную мастерскую оставить отдельным дополнением для тех, кто хочет изменить пример.
Windows: запустить, изменить и собрать проект
Установите Node.js LTS с официального сайта, распакуйте архив в обычный рабочий каталог и откройте PowerShell в shiki-project. Сначала проверьте версию, затем выполните npm ci. Эта команда использует lock-файл проекта; ручное обновление зависимостей до случайного набора версий для первого запуска не требуется.
После тестов соберите public и запустите локальный сервер. Адрес мастерской — http://127.0.0.1:8524/. Остановка выполняется Ctrl+C в том же окне. Сервер слушает loopback и предназначен для локального просмотра. Если порт занят, разберитесь, какой процесс его использует, или настройте свой порт в сервере; останавливать чужое приложение ради примера не нужно.
Для просмотра уже подготовленной папки public можно использовать любой подходящий локальный HTTP-сервер, в том числе встроенный сервер установленного PHP. PHP здесь необязателен. Открытие index.html через file:// не поддерживается: мастерская загружает локальные JSON-ресурсы обычными HTTP-запросами.
node --version
npm ci --ignore-scripts
npm test
npm run build
npm run serve
# Альтернатива только для просмотра готовой public:
# php -S 127.0.0.1:8524 -t public
Linux: повторить сборку и подготовить каталог публикации
В Linux команды проекта остаются теми же. Используйте Node.js 24 LTS подходящей архитектуры и выполняйте npm от обычного пользователя. Не переносите node_modules из Windows: сборщик esbuild использует платформенные компоненты, а npm ci устанавливает зависимости для текущего окружения. Переносить следует исходники и lock-файл.
Для производственного сайта сборку удобно выполнять в CI или отдельном каталоге deploy-пользователя. После успешных тестов публикуется содержимое public. Исходные тесты, node_modules и временные материалы не нужны в web-root. В нашей public намеренно присутствует просмотр исходников мастерской; если используете проект для закрытого продукта, отдельно определите, какие материалы должны быть доступны читателю.
Постоянный процесс Node для готовой документации не требуется. Он нужен на этапе подготовки и локального просмотра, а Nginx или Caddy может отдавать результат как обычную статику. Это упрощает эксплуатацию: обновление страницы означает выпуск нового набора файлов, а не запуск отдельного приложения подсветки для каждого посетителя.
cd shiki-project
node --version
npm ci --ignore-scripts
npm test
npm run build
# Для локальной проверки:
npm run serve
Linux/VPS: отдать мастерскую через Nginx или Caddy
Создайте отдельный каталог и настройте виртуальный хост своего домена. В примере используется стандартная для Debian/Ubuntu группа www-data; на другой системе подставьте свою. Копировать нужно содержимое public после успешной сборки. Перед reload проверяйте конфигурацию, чтобы опечатка в новой странице не нарушила существующие сайты.
Ниже приведён HTTP-вариант Nginx для понятного начала. Для публичного сайта настройте HTTPS с реальным доменом, DNS и подходящим механизмом ACME. Воспроизводимость локальной сборки не проверяет внешний firewall, выпуск или продление сертификата вашего VPS. Эти этапы подтверждаются уже в выбранном окружении.
Подробная эксплуатация разобрана в материалах про Nginx и Caddy с HTTPS. Для Shiki важен простой результат: правильный web-root, доступные JSON/CSS/JS и отсутствие публикации служебных файлов. После загрузки проверьте также пути к prebuilt и архиву исходников.
sudo install -d -m 0755 -o "$USER" -g www-data /srv/shiki-demo/public
cp -R public/. /srv/shiki-demo/public/
# Создайте отдельную server-конфигурацию, затем:
sudo nginx -t
sudo systemctl reload nginx
server {
listen 80;
server_name docs.example.test;
root /srv/shiki-demo/public;
index index.html;
add_header X-Content-Type-Options nosniff always;
location / { try_files $uri $uri/ =404; }
}
docs.example.com {
root * /srv/shiki-demo/public
file_server
}
# Подставьте свой настоящий домен и выполните проверку Caddyfile.
# DNS и доступность портов зависят от вашего сервера.
Проверка после изменения: смысл кода, удобство и опубликованная страница
Проект содержит 11 тестов настоящего Shiki. Они проверяют пять грамматик и семь исходных примеров, диапазоны, ограничения ввода, экранирование HTML, diff и диагностические пометки, смещение нумерации, повторное использование highlighter и автономный экспорт. Набор выполнен в Windows и Linux. Это проверка реализации и результата обработки, а не замена браузерному просмотру.
После собственной правки откройте мастерскую на узком экране. Убедитесь, что выбор задачи, кнопка подсветки и сообщения остаются понятными, код прокручивается внутри блока, а экспорт доступен после успешной обработки. Проверьте клавиатурный фокус и текст ошибки диапазона. Отдельно посмотрите вторую тему: контраст может измениться даже при небольшом обновлении CSS.
На опубликованной странице проверьте загрузку JS, CSS, manifest и исходников, затем скачайте один HTML и исходный файл. В седьмой задаче откройте каждый готовый артефакт. После изменения src/cases.mjs старая сборка может выглядеть исправной, но содержать прежний текст, поэтому пересборка и проверка загруженных файлов являются частью выпуска.
npm test
npm run build
# Проверки находятся в tests/highlight.test.mjs.
# Артефакты и контрольные суммы — public/prebuilt/manifest.json.
# Размеры текущей сборки — public/build-info.json.
Скачать исходники и применить к своей документации
Архив содержит HTML5/Bootstrap-интерфейс, семь сценариев, обёртку над Shiki, тесты, сборку, статические примеры и инструкции Windows/Linux/VPS. Авторский код распространяется по MIT; лицензии зависимостей перечислены в THIRD-PARTY-LICENSES.txt. Готовая public позволяет сначала попробовать результат, затем постепенно разбирать реализацию.
Начните с одной полезной страницы своего продукта: запроса к API, настройки сервера или инструкции по исправлению ошибки. Выберите язык, напишите короткое пояснение, выделите действительно важные строки и проверьте копирование. Для неизменяемого материала подготовьте HTML при сборке. Если читателю нужно редактировать пример, добавьте интерактивную обработку и понятные границы результата.
Официальные руководства Shiki, на которые ссылаются разделы статьи, помогут расширить набор языков, тем и transformers. Практический критерий остаётся простым: посетитель должен понять назначение фрагмента, увидеть важное и получить исходный текст без лишних действий. Подсветка приносит пользу тогда, когда делает применение примера удобнее.


