esbuild не содержит встроенной опции alias в
конфигурации наподобие Webpack или Vite, однако механизм переопределения
путей реализуется через плагины, основанные на хуках
onResolve. Такой подход обеспечивает гибкость и позволяет
управлять резолвингом модулей на уровне системы сборки.
Процесс обработки импортов в esbuild проходит через стадию резолвинга, на которой каждый импортированный модуль преобразуется в физический путь или виртуальный модуль. На этой стадии могут вмешиваться плагины, изменяющие поведение стандартного алгоритма поиска файлов.
Ключевой хук:
onResolve — перехватывает пути импортов до их
обработкиfilter — задаёт правила перехвата (регулярные
выражения)namespace — разделяет области виртуальных модулейИменно через onResolve реализуется поведение,
аналогичное alias.
Переопределение путей строится на сопоставлении префикса импорта с целевым абсолютным или относительным путём.
import path from "path";
const aliasPlugin = (aliases) => ({
name: "alias-plugin",
setup(build) {
for (const [find, replacement] of Object.entries(aliases)) {
const filter = new RegExp(`^${find}(/.*)?$`);
build.onResolve({ filter }, (args) => {
const replacedPath = args.path.replace(find, replacement);
return {
path: path.resolve(replacedPath),
};
});
}
},
});
Использование:
import esbuild from "esbuild";
esbuild.build({
entryPoints: ["src/index.js"],
bundle: true,
outfile: "dist/app.js",
plugins: [
aliasPlugin({
"@components": "./src/components",
"@utils": "./src/utils",
}),
],
});
При импорте:
import Button from "@components/Button";
плагин выполняет следующие шаги:
onResolve при совпадении с регулярным
выражением@components)Alias должен корректно обрабатывать поддиректории:
@components/ui/Button
@components/layout/Header
Корректная реализация сохраняет остаточную часть пути:
const filter = new RegExp(`^${find}(\\/.*)?$`);
build.onResolve({ filter }, (args) => {
const rest = args.path.slice(find.length);
return {
path: path.join(replacement, rest),
};
});
Alias может указывать как на относительные директории проекта, так и на абсолютные пути файловой системы.
Абсолютный вариант:
const aliases = {
"@root": path.resolve("./src"),
};
Относительный вариант:
const aliases = {
"@api": "./src/api",
};
При использовании относительных путей важно нормализовать путь через
path.resolve, чтобы избежать неоднозначности при
сборке.
Плагины onResolve выполняются до стандартного резолвинга
esbuild. Это означает:
Структура приоритетов:
onResolveПри пересечении alias-правил возможны неоднозначности:
{
"@app": "./src/app",
"@app/utils": "./src/shared/utils"
}
В данном случае важно учитывать порядок проверки, иначе более общий alias может перехватить более специфичный путь.
Практика:
Alias в esbuild не влияет автоматически на TypeScript. Для
синхронизации требуется настройка tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"]
}
}
}
Без этой настройки IDE и компилятор TypeScript будут расходиться с esbuild.
Alias одинаково применяется как для ESM, так и для CommonJS модулей, поскольку обработка происходит до определения формата модуля.
Примеры:
import x from "@utils/math";
const y = require("@utils/math");
Оба варианта проходят через один механизм onResolve.
В сложных проектах alias может ссылаться на другие alias-пути. В таком случае требуется нормализация, чтобы избежать каскадных замен.
Рекомендуемый подход — финальная резолюция через абсолютные пути:
const aliases = {
"@models": path.resolve("./src/models"),
"@services": path.resolve("./src/services"),
};
В некоторых архитектурах alias применяется не к файлам, а к виртуальным модулям:
build.onResolve({ filter: /^virtual:/ }, () => ({
namespace: "virtual-modules",
path: "virtual",
}));
Это позволяет создавать логические alias, не связанные с файловой системой напрямую.
Поскольку alias реализуется через плагины:
Тем не менее гибкость системы позволяет реализовать поведение, аналогичное любым bundler-решениям, включая многоуровневые пространства имён и динамическую маршрутизацию модулей.