Избранные проекты
Назад к проектам

Браузерные редакторы

Редактор кода на Monaco для Liquid-шаблонов inSales

Общая сборка Monaco для смешанного HTML, Liquid, CSS и JavaScript работает в пяти продуктовых интерфейсах inSales. Сделал первую интеграцию и больше трёх лет был основным мейнтейнером.

  • 5 продуктовых интерфейсов inSales работают на одной сборке редактора
  • 3+ года был основным мейнтейнером, с июля 2022 по октябрь 2025
Моя роль
Вёл разработку: языковая поддержка, браузерный API, сборка, интеграции
Период
Июль 2022 – ноябрь 2025
Навыки
  • Monaco Editor
  • TypeScript
  • Liquid
  • JavaScript
  • Front-End Architecture
  • Vue.js
  • Webpack
  • API Integration
  • CI/CD
  • Gitlab
  • Ruby on Rails
  • SCSS
  • HTML
  • Developer Tools
  • Software Maintenance
  • Syntax Highlighting
  • Autocomplete
Редактор кода виджета с шестью вкладками файлов и подсказкой Liquid-тега for внутри HTML-разметки
На этой странице

Задача

В шаблонах витрин inSales в одном файле живут HTML, Liquid, JavaScript и CSS, а настройки и переводы хранятся в JSON. Этот код в браузере, прямо в админке, правят партнёры-разработчики, сотрудники поддержки и технически подкованные владельцы магазинов, не скачивая шаблон в IDE.

Прежняя интеграция CodeMirror умела открыть и сохранить код, но не подсвечивала смешанный синтаксис, не подсказывала с учётом позиции курсора, не знала Emmet и сниппетов inSales. Чтобы довести её до нормальной рабочей среды, языковую часть всё равно пришлось бы проектировать заново. Поэтому выбрали Monaco — движок, на котором построен VS Code, — а кастомную сборку и её внедрение поручили мне.

Моя роль

С июля 2022 по октябрь 2025 года вёл разработку редактора. Сделал первую интеграцию для кода виджетов, потом вынес её в общий пакет и оставался его основным мейнтейнером, пока редактор подключали к новым экранам. На мне были языковая поддержка, браузерный API, упаковка, настройка релизов и крупные интеграции. Сам движок редактирования дал Monaco. Коллеги помогали с путями к воркерам, обновлением темы и форматированием кода.

Что я сделал

Токенизатор для шаблонов, где HTML смешан с Liquid

Liquid стоит прямо в разметке: вывод, управляющие теги, комментарии, блоки raw, фильтры inSales. Он бывает и внутри значения HTML-атрибута, целиком или вперемешку с обычным текстом. Я зарегистрировал собственный язык HTML + Liquid с токенизатором Monarch и языковой конфигурацией. После закрывающего разделителя внутри атрибута токенизатор должен вернуться в состояние атрибута, а не красить остаток строки как Liquid. Внутри script он переключается на JavaScript или JSON, внутри style — на CSS, а после закрывающего тега возвращается к HTML. Пары скобок, автозакрытие и правила отступов дают файлу вести себя как один язык. Часть проблем видна только в реальном продукте: один вариант регулярного выражения полностью ломал редактор в Safari, поэтому в токенизаторе от него отказались.

Подсказки зависят от языка под курсором

Внутри script нужны подсказки JavaScript, а не HTML-атрибуты и Liquid-сниппеты, внутри style — CSS. Первая версия определяла контекст по ближайшим открывающему и закрывающему тегам перед курсором. В 2024 году я заменил эту проверку на getLanguageIdAtPosition из самого Monaco: модель уже знает, какой вложенный язык стоит в этой позиции. Заодно нормализовал значения script type вроде application/json и application/ld+json, чтобы редкие типы не пытались загрузить несуществующий языковой сервис.

Полный редактор шаблона: дерево файлов и подсказка querySelector внутри блока script в layouts.layout.liquid

Подсказки JavaScript внутри script в Liquid-файле лейаута.

Браузерный API из трёх функций

Чтобы редактор не принадлежал одному модальному окну, я сделал три точки входа: первая возвращает загруженный экземпляр Monaco, вторая создаёт редактор в переданном элементе с общими настройками, третья регистрирует конфигурации Liquid, JavaScript и SCSS для экранов, которые сами создают модели. Перед регистрацией языка сборка проверяет, не добавлен ли он уже, поэтому на странице с несколькими точками входа провайдеры не дублируются. Загрузка, сохранение и предпросмотр остаются за каждым продуктовым экраном. Поверх API я написал Vue-компонент для виджета с шестью файлами: разметка, SCSS, JavaScript, переводы, схема формы настроек и значения по умолчанию.

Воркеры и упаковка тоже часть контракта

Языковой анализ в Monaco выполняется в Web Workers, поэтому одного основного бандла для работающего редактора мало. Я настроил Webpack так, чтобы он собирал бандл и пять языковых воркеров, и упаковал их в Rails Engine и внутренний gem. GitLab CI собирает бандл, собирает gem и публикует версию во внутренний реестр пакетов. Основное приложение подключает конкретную версию, а исправление токенизатора или новая команда выходят сразу во все экраны.

Одна сборка для шаблонов и сообщений покупателям

После кода виджетов ту же сборку подключили к полному редактору шаблона с деревом файлов, затем к письмам по заказам, напоминаниям о брошенных корзинах и уведомлениям о статусе заказа в «Диалогах». В этих разделах продавец правит HTML/Liquid или текст с Liquid-переменными и проверяет результат в предпросмотре на реальном заказе или корзине.

Редактор шаблона письма клиенту: тема, исходный код на HTML и Liquid, предпросмотр по номеру заказа

Результат

  • Одна сборка редактора работает в 5 продуктовых интерфейсах inSales: код виджета, полный шаблон витрины, письма по заказам, сообщения о брошенных корзинах и уведомления «Диалогов».
  • Больше 3 лет, с июля 2022 по октябрь 2025 года, был основным мейнтейнером.
  • Общие доработки делаются один раз и попадают во все интерфейсы: коллеги добавили форматирование Liquid и обновили тему в пакете, не переделывая каждый экран.
  • Продавцы и партнёры редактируют код в админке примерно как в VS Code: подсказки по контексту, Emmet, сниппеты inSales и комментирование Liquid по Ctrl+Shift+/ с нормальной отменой и повтором.

Написать мне

Ищете senior frontend-разработчика?

Готов к удалённой работе и релокации. Напишите мне или запишитесь на звонок.

Написать мне