Опция entryNames: шаблоны имён выходных файлов

Опция entryNames в Esbuild управляет шаблоном именования выходных файлов, создаваемых из входных точек входа (entry points). Она используется только при включённом режиме раздельной генерации файлов через параметр outdir и позволяет гибко формировать структуру итоговой сборки.

По умолчанию Esbuild сохраняет имя выходного файла на основе имени исходного файла. Однако в крупных проектах этого часто недостаточно. Возникает необходимость:

  • разделять файлы по каталогам;
  • добавлять хеши для кэширования;
  • избегать конфликтов имён;
  • формировать предсказуемую структуру сборки;
  • организовывать ресурсы по типам.

Опция entryNames решает эти задачи посредством специальных шаблонов.


Базовый синтаксис

await esbuild.build({
    entryPoints: ['src/index.js'],
    bundle: true,
    outdir: 'dist',
    entryNames: '[name]'
});

В данном случае имя результирующего файла будет сформировано из имени входного файла:

src/index.js
↓
dist/index.js

Как работает шаблон

Значение entryNames представляет собой строку-шаблон, содержащую специальные плейсхолдеры.

Пример:

entryNames: 'js/[name]-[hash]'

Результат:

dist/
└── js/
    └── index-A3F7K2.js

Во время сборки Esbuild заменяет плейсхолдеры реальными значениями.


Плейсхолдер [name]

Плейсхолдер [name] подставляет имя файла без расширения.

Исходный файл:

src/main.js

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

entryNames: '[name]'

Результат:

main.js

Другой пример:

src/admin.js

Результат:

admin.js

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

entryNames: 'pages/[name]'

Результат:

dist/
└── pages/
    └── main.js

Плейсхолдер [hash]

[hash] добавляет уникальный хеш содержимого файла.

entryNames: '[name]-[hash]'

Результат:

index-R4JK9Q.js

После изменения кода:

index-B72FKM.js

Зачем нужен хеш

Основное назначение — управление кэшированием браузера.

Без хеша:

app.js

После обновления приложения имя остаётся прежним:

app.js

Браузер может использовать старую закэшированную версию.

С хешем:

app-W3J4K8.js

После изменения:

app-X8N2L1.js

Поскольку имя файла изменилось, браузер гарантированно загрузит новую версию.

Такой подход называется cache busting.


Плейсхолдер [dir]

[dir] сохраняет структуру каталогов относительно базовой директории.

Рассмотрим проект:

src/
├── admin/
│   └── index.js
└── user/
    └── index.js

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

entryNames: '[dir]/[name]'

Результат:

dist/
├── admin/
│   └── index.js
└── user/
    └── index.js

Без использования [dir] возник бы конфликт:

dist/
└── index.js

Поскольку оба файла имеют одинаковое имя.


Использование нескольких плейсхолдеров

Плейсхолдеры можно комбинировать.

Пример:

entryNames: '[dir]/[name]-[hash]'

Результат:

dist/
├── admin/
│   └── index-T6A9K2.js
└── user/
    └── index-P9F4D7.js

Формирование собственной структуры каталогов

Часто требуется размещать итоговые файлы в отдельных папках.

Пример для JavaScript

entryNames: 'assets/js/[name]'

Результат:

dist/
└── assets/
    └── js/
        └── main.js

С хешем

entryNames: 'assets/js/[name]-[hash]'

Результат:

dist/
└── assets/
    └── js/
        └── main-X8K2L9.js

Работа с несколькими entry points

Исходные данные:

entryPoints: [
    'src/home.js',
    'src/about.js',
    'src/contact.js'
]

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

entryNames: '[name]-bundle'

Результат:

home-bundle.js
about-bundle.js
contact-bundle.js

Каждая точка входа получает собственное имя согласно шаблону.


Практический пример для многостраничного сайта

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

src/
├── pages/
│   ├── home.js
│   ├── blog.js
│   └── contacts.js

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

await esbuild.build({
    entryPoints: [
        'src/pages/home.js',
        'src/pages/blog.js',
        'src/pages/contacts.js'
    ],
    bundle: true,
    outdir: 'dist',
    entryNames: 'pages/[name]-[hash]'
});

Результат:

dist/
└── pages/
    ├── home-AB123C.js
    ├── blog-FF821D.js
    └── contacts-91AD11.js

Подобная схема часто используется в production-сборках.


Влияние на импортируемые чанки

Важно понимать различие между:

  • entryNames
  • chunkNames

entryNames влияет только на файлы, являющиеся точками входа.

Например:

entryPoints: ['src/index.js']

Файл:

import('./module.js');

Если включено разделение кода:

splitting: true

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

Пример:

entryNames: '[name]-[hash]',
chunkNames: 'chunks/[name]-[hash]'

Результат:

dist/
├── index-D81F3A.js
└── chunks/
    └── chunk-M9L1K2.js

Влияние на CSS-файлы

Если из точки входа генерируется отдельный CSS-файл:

import './styles.css';

Esbuild создаёт CSS-ресурс, связанный с соответствующим entry-файлом.

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

entryNames: '[name]-[hash]'

Результат:

dist/
├── app-F3K1Q8.js
└── app-F3K1Q8.css

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


Использование вместе с outbase

Плейсхолдер [dir] особенно полезен в сочетании с параметром outbase.

Структура:

src/
├── admin/index.js
└── shop/index.js

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

await esbuild.build({
    entryPoints: [
        'src/admin/index.js',
        'src/shop/index.js'
    ],
    outbase: 'src',
    outdir: 'dist',
    bundle: true,
    entryNames: '[dir]/[name]'
});

Результат:

dist/
├── admin/
│   └── index.js
└── shop/
    └── index.js

outbase определяет точку отсчёта для вычисления значения [dir].


Популярные шаблоны

Простое имя файла

entryNames: '[name]'

Результат:

main.js

Имя с хешем

entryNames: '[name]-[hash]'

Результат:

main-A8D2F4.js

Отдельная папка для ресурсов

entryNames: 'assets/[name]-[hash]'

Результат:

assets/main-A8D2F4.js

Сохранение структуры каталогов

entryNames: '[dir]/[name]'

Результат:

admin/index.js
user/index.js

Структура каталогов и хеш

entryNames: '[dir]/[name]-[hash]'

Результат:

admin/index-A8D2F4.js
user/index-C9L7P1.js

Типичные сценарии использования

Разработка

Во время разработки важны простота и предсказуемость.

Часто используется:

entryNames: '[name]'

или

entryNames: '[dir]/[name]'

Это облегчает поиск файлов и отладку.


Production

Для производственных сборок почти всегда применяется хеширование:

entryNames: '[name]-[hash]'

или

entryNames: 'assets/js/[name]-[hash]'

Так обеспечивается корректное обновление ресурсов после деплоя.


Крупные монорепозитории

Для больших проектов характерна вложенная структура каталогов:

entryNames: '[dir]/[name]-[hash]'

Такой подход:

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

Ограничения и особенности

Опция entryNames работает только для точек входа.

Не влияет на:

  • чанки, созданные через code splitting;
  • файлы, контролируемые assetNames;
  • дополнительные выходные ресурсы, использующие собственные шаблоны именования.

Для полной настройки структуры сборки обычно совместно используются:

entryNames
chunkNames
assetNames

Пример комплексной конфигурации:

await esbuild.build({
    entryPoints: ['src/index.js'],
    bundle: true,
    splitting: true,
    format: 'esm',
    outdir: 'dist',

    entryNames: 'js/[name]-[hash]',
    chunkNames: 'chunks/[name]-[hash]',
    assetNames: 'assets/[name]-[hash]'
});

Результирующая структура:

dist/
├── js/
│   └── index-AB12CD.js
├── chunks/
│   └── chunk-EF34GH.js
└── assets/
    ├── logo-XY98ZT.svg
    └── font-LM44NP.woff2

Опция entryNames является одним из ключевых инструментов управления выходной структурой Esbuild. Благодаря поддержке шаблонов [name], [hash] и [dir] можно строить как простые схемы именования для разработки, так и сложные production-конфигурации с сохранением структуры каталогов и эффективным кэшированием ресурсов.