Опция serve: host, port, servedir

Опция serve в Esbuild предназначена для запуска встроенного HTTP-сервера, который обслуживает файлы проекта и автоматически пересобирает приложение при изменении исходного кода. Такой режим особенно полезен во время разработки, когда требуется быстро получать результаты сборки без настройки стороннего веб-сервера.

В отличие от полноценного dev-сервера современных инструментов вроде Vite или Webpack Dev Server, встроенный сервер Esbuild выполняет ограниченный набор задач:

  • запускает HTTP-сервер;
  • отдает статические файлы;
  • обслуживает результаты сборки;
  • работает совместно с режимом отслеживания изменений (watch);
  • обеспечивает высокую скорость запуска.

Основные параметры настройки сервера:

  • host — сетевой интерфейс, на котором будет запущен сервер;
  • port — номер TCP-порта;
  • servedir — каталог со статическими файлами.

Общая структура использования

Пример запуска сервера через JavaScript API:

import * as esbuild from 'esbuild';

await esbuild.context({
    entryPoints: ['src/index.js'],
    bundle: true,
    outfile: 'dist/app.js'
}).then(async (ctx) => {
    await ctx.serve({
        servedir: 'dist',
        host: 'localhost',
        port: 3000
    });
});

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

http://localhost:3000

Если в каталоге dist присутствует файл index.html, он будет отдан браузеру при обращении к корню сайта.


Параметр servedir

Назначение

Параметр servedir определяет директорию, содержимое которой будет доступно через HTTP-сервер.

Пример:

await ctx.serve({
    servedir: 'public'
});

Структура проекта:

project/
├── public/
│   ├── index.html
│   ├── styles.css
│   └── images/
│       └── logo.png
└── src/

После запуска будут доступны:

http://localhost:8000/
http://localhost:8000/styles.css
http://localhost:8000/images/logo.png

Связь с результатами сборки

Наиболее распространённый сценарий — размещение результатов сборки в каталоге, который одновременно выступает в роли servedir.

await esbuild.context({
    entryPoints: ['src/main.js'],
    bundle: true,
    outdir: 'dist'
});
await ctx.serve({
    servedir: 'dist'
});

Структура после сборки:

dist/
├── index.html
├── main.js
└── main.css

Все файлы автоматически становятся доступными через HTTP.


Использование отдельного каталога

Иногда статические ресурсы и результаты сборки располагаются в разных местах.

Пример:

project/
├── public/
│   └── index.html
├── dist/
│   └── bundle.js
└── src/

Конфигурация:

await esbuild.context({
    entryPoints: ['src/app.js'],
    bundle: true,
    outfile: 'dist/bundle.js'
});

Если указать:

await ctx.serve({
    servedir: 'public'
});

то файл bundle.js окажется недоступен.

Поэтому чаще применяют одну из схем:

  1. Генерировать сборку прямо в public.
  2. Копировать результаты сборки в каталог сервера.
  3. Использовать единую директорию вывода.

Например:

outfile: 'public/bundle.js'

Автоматический поиск index.html

При обращении к каталогу сервер пытается найти файл:

index.html

Пример структуры:

dist/
├── index.html
├── about/
│   └── index.html

Тогда адреса будут работать следующим образом:

/
→ dist/index.html

/about/
→ dist/about/index.html

Это позволяет создавать многостраничные приложения без дополнительной конфигурации маршрутизации.


Отображение содержимого каталога

Если файл index.html отсутствует, сервер отображает список файлов директории.

Например:

dist/
├── app.js
├── styles.css
└── image.png

При открытии:

http://localhost:8000/

будет показан список доступных файлов.

Такое поведение удобно для отладки и быстрого просмотра содержимого каталога сборки.


Параметр host

Назначение

Параметр host определяет сетевой интерфейс, на котором будет прослушиваться сервер.

Пример:

await ctx.serve({
    servedir: 'dist',
    host: 'localhost'
});

В этом случае подключения принимаются только локально.


Значение localhost

Самый распространённый вариант:

host: 'localhost'

или

host: '127.0.0.1'

Доступ:

http://localhost:8000

Такой режим обеспечивает дополнительную безопасность, поскольку сервер не будет виден другим устройствам сети.

Подходит для:

  • локальной разработки;
  • тестирования на одной машине;
  • работы с конфиденциальными данными.

Значение 0.0.0.0

Для доступа с других устройств локальной сети используется:

host: '0.0.0.0'

Пример:

await ctx.serve({
    servedir: 'dist',
    host: '0.0.0.0',
    port: 3000
});

После запуска сервер становится доступен по IP-адресу компьютера:

http://192.168.1.15:3000

Теперь страницу можно открыть:

  • с телефона;
  • с планшета;
  • с другого компьютера сети;
  • с виртуальной машины;
  • с контейнера Docker.

Использование конкретного IP

Иногда требуется привязка к определённому интерфейсу.

Пример:

host: '192.168.1.15'

Тогда сервер будет слушать только этот адрес.

Подобный подход применяется при наличии нескольких сетевых интерфейсов:

Ethernet
Wi-Fi
VPN
Docker Network
VirtualBox Adapter

Работа с IPv6

Esbuild поддерживает IPv6-адреса.

Пример:

host: '::1'

Локальное подключение:

http://[::1]:8000

Для всех IPv6-интерфейсов:

host: '::'

Вопросы безопасности

Использование:

host: '0.0.0.0'

открывает доступ извне.

Поэтому необходимо учитывать:

  • настройки файрвола;
  • сетевую инфраструктуру;
  • VPN-подключения;
  • наличие открытых портов.

Для обычной локальной разработки предпочтительно:

host: 'localhost'

Параметр port

Назначение

Параметр port задаёт номер порта, на котором будет запущен HTTP-сервер.

Пример:

await ctx.serve({
    servedir: 'dist',
    port: 3000
});

После запуска:

http://localhost:3000

Использование стандартного порта

По умолчанию Esbuild пытается подобрать свободный порт автоматически.

Однако явное указание порта делает адрес сервера предсказуемым.

Пример:

port: 8080

Адрес:

http://localhost:8080

Популярные значения

Во время разработки часто используются следующие порты:

Порт Назначение
3000 React-приложения
4000 Вспомогательные сервисы
5000 API и тестовые серверы
8000 Значение по умолчанию во многих инструментах
8080 Альтернатива 80 порту

Пример:

await ctx.serve({
    servedir: 'dist',
    port: 8080
});

Конфликт портов

Если порт уже используется другим процессом:

port: 3000

и на этом адресе уже работает приложение, запуск завершится ошибкой.

Типичная ситуация:

EADDRINUSE
Address already in use

Возможные решения:

  • остановить конфликтующий процесс;
  • выбрать другой порт;
  • использовать автоматический выбор свободного порта.

Получение фактического порта

Метод serve() возвращает информацию о запущенном сервере.

Пример:

const result = await ctx.serve({
    servedir: 'dist'
});

console.log(result);

Результат может выглядеть так:

{
    hosts: ['127.0.0.1'],
    port: 8000
}

Это особенно полезно при автоматическом выборе свободного порта.


Совместное использование host, port и servedir

Наиболее типичная конфигурация:

import * as esbuild from 'esbuild';

const ctx = await esbuild.context({
    entryPoints: ['src/index.js'],
    bundle: true,
    outdir: 'dist'
});

await ctx.watch();

const server = await ctx.serve({
    servedir: 'dist',
    host: 'localhost',
    port: 3000
});

console.log(server);

Особенности данной конфигурации:

  • файлы собираются в dist;
  • изменения отслеживаются автоматически;
  • сервер работает на localhost;
  • используется порт 3000;
  • браузер получает актуальные версии файлов после каждой пересборки.

Использование с режимом watch

На практике serve() почти всегда применяется совместно с watch().

Пример:

const ctx = await esbuild.context({
    entryPoints: ['src/main.js'],
    bundle: true,
    outdir: 'dist'
});

await ctx.watch();

await ctx.serve({
    servedir: 'dist'
});

Последовательность работы:

  1. Выполняется первоначальная сборка.
  2. Запускается HTTP-сервер.
  3. Esbuild отслеживает изменения файлов.
  4. После сохранения исходников выполняется пересборка.
  5. Сервер начинает отдавать новые версии файлов.

Таким образом формируется полноценная среда разработки с минимальным количеством настроек и очень высокой скоростью работы.


Возвращаемое значение метода serve

Метод возвращает объект с параметрами работающего сервера.

Пример:

const info = await ctx.serve({
    servedir: 'dist',
    host: '0.0.0.0',
    port: 3000
});

console.log(info);

Результат:

{
    hosts: [
        '127.0.0.1',
        '192.168.1.15'
    ],
    port: 3000
}

Поле hosts содержит адреса, по которым сервер доступен в текущей системе, а поле port указывает фактически используемый номер порта.

Эта информация часто применяется для автоматического открытия браузера, вывода адресов в консоль и интеграции с инструментами разработки.