Тема сайта собирается webpack прямо на сервере. За год это дало три урока: сборка падает на новой версии Node, линтер, запущенный «по всему», висит сутками даже после прерывания, а CI находит ошибку, которую сборка на тесте не видела ни разу.
Это 13-я часть цикла «Сервер для WordPress».
1. Node 22 ломает сборку
[webpack-cli] SyntaxError: Unexpected identifier 'assert'
Один из плагинов webpack импортирует JSON старым синтаксисом:
import schema from './schemas/plugin-options.json' assert { type: 'json' };
В Node 22 этот синтаксис удалён (новый — with { type: 'json' }), и webpack не может даже загрузить конфиг. Node 22 падает так же, как 24, — первая попытка «поставить LTS поновее» не помогла. Собралось только на Node 20.
Править файл внутри node_modules бесполезно — правка слетит при следующей установке пакетов. Варианты: держать Node 20, зафиксировать правку через patch-package или обновить плагин, когда выйдет версия с новым синтаксисом. Мы держим Node 20.
Node — в контейнере, а не на сервере
На сервере живут проекты, которым нужны разные версии Node. Ставить Node в систему — значит выбирать, какой из них сломать. Поэтому node и npm на сервере — обёртки, которые запускают официальный образ node:20 с папкой проекта:
docker run --rm -v "$PWD":/app -w /app node:20-bookworm npm run build
Версия Node становится свойством команды, а не сервера.
2. Линтер, который висит сутками
Запуск eslint src/ по всей теме на тестовом сервере занял ядро на 101% и не заканчивался. Вызов прервали — а процесс остался жить: он работал в контейнере, запущенном с --rm, и прерывание клиента контейнер не останавливает.
# найти и остановить прогон, который остался после прерывания
docker ps --format '{{.ID}} {{.Command}}' | grep -i eslint
docker kill <id> # именно контейнер: kill процесса на хосте его не остановит
Не убивайте node на сервере всем скопом: там же работают сборки в режиме наблюдения у других проектов.
3. CI видит больше, чем сборка на тесте
Изменения ушли в репозиторий — и выкат остановился на ошибке форматирования в файле, который сборка на тесте собирала много раз без замечаний.
Причина — настройка сборки в режиме наблюдения: линтер проверяет только изменённые модули (lintDirtyModulesOnly). Файл правили до того, как запустили наблюдение, поэтому «изменённым» он для сборки не стал ни разу. А CI ставит пакеты с нуля и проверяет весь src.
Что именно ругалось: при ширине строки 150 символов многострочная стрелочная функция, которая помещается в одну строку, обязана быть в одну строку.
// prettier потребует собрать в одну строку — она влезает
['scroll', 'touchstart'].forEach((type) =>
window.addEventListener(type, open, { once: true, passive: true })
);
// однозначно при любой ширине строки
for (const type of ['scroll', 'touchstart']) {
window.addEventListener(type, open, { once: true, passive: true });
}
Правило: перед коммитом прогнать линтер по всему src так же, как CI. Повторные прогоны во время работы — только по изменённым файлам, чтобы не повторять историю с зависшим контейнером.
docker run --rm -v "$PWD":/app -w /app node:20-bookworm npx eslint src
Сборка темы падает на сервере или в CI?
Пришлите ошибку — найду, в версии Node дело, в линтере или в том, что CI видит больше.
// загружаю комментарии…