Плагин CLI
Рекомендуемое решение для webpack: true. Работает автоматически, на каждой сборке, и о нём не нужно помнить как об отдельном шаге.
Тем же механизмом пользуется собственный плагин CLI из @nestjs/swagger, чтобы работать под webpack: true. Именно поэтому @ApiProperty() не нужен у каждого свойства DTO даже в сборке через webpack. Nest CLI одинаково передаёт хук плагина компилятора TypeScript (compilerOptions.plugins) и в сборщик tsc, и в webpack через ts-loader.
Регистрация плагина
Пропишите nestjs-docfy в nest-cli.json:
{
"compilerOptions": {
"webpack": true,
"plugins": ["nestjs-docfy"]
}
}import { applyDocfyMetadata } from 'nestjs-docfy';
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, applyDocfyMetadata(document));| Option | Type | Default | Description |
|---|---|---|---|
metadataPath | string | docfy-metadata.json next to the running entry file | Where to read the plugin-generated metadata from. |
strict | boolean | false | Throw instead of warning when the metadata file is missing or invalid. |
Автоматическое определение
Забыть зарегистрировать плагин легко, и обнаруживается это обычно уже после сборки без документации. Поэтому каждый запуск generate заглядывает в nest-cli.json и предупреждает, когда webpack: true стоит, а плагина нет. Передайте --register-plugin, чтобы команда поправила это сама, без ручной правки файла:
npx nestjs-docfy generate --register-pluginФлаг включается вручную намеренно: часть команд сознательно остаётся на patch-spec вместо плагина компилятора, поэтому generate никогда не трогает nest-cli.json без явной просьбы. Команда дописывает запись в уже существующий массив compilerOptions.plugins (распознавая как зарегистрированные и строковые записи, и записи-объекты) и учитывает --dry-run.
Как это работает
Плагин nestjs-docfy не переписывает синтаксис декораторов и вообще не трогает AST. На каждой компиляции он заново прогоняет ровно тот же статический анализ, который уже делают generate, check и patch-spec (через ts-morph, по дереву исходников, а не по бандлу), и записывает получившуюся правку в docfy-metadata.json рядом с результатом сборки.
Затем, сразу после SwaggerModule.createDocument(), applyDocfyMetadata() читает этот файл и вливает его содержимое. Никакого require.cache здесь нет, как нет и проблемы с идентичностью классов в бандле: чтобы всё посчитать, запущенное приложение вообще не нужно.
Сравнение с patch-spec
Если добавлять плагин компилятора не хочется (скажем, сборка идёт не через Nest CLI или у вас строже правила насчёт того, что выполняется во время компиляции), patch-spec считает точно такую же правку вручную по уже собранному документу. До появления плагина это был единственный вариант, и для разовых правок он по-прежнему удобен.
Сборщик SWC
Со сборщиком SWC Nest CLI обходится иначе. Под "builder": "swc" он вообще не вызывает before(), только ReadonlyVisitor, и лишь при заданном "typeCheck": true (иначе SWC пропускает проверку типов сам по себе). Этот плагин экспортирует оба хука, так что достаточно включить упомянутую настройку:
{
"compilerOptions": {
"builder": "swc",
"typeCheck": true,
"plugins": ["nestjs-docfy"]
}
}Стоит знать одну деталь: при такой регистрации на каждой сборке рядом с корнем исходников появляется файл metadata.ts. Это тот же артефакт, который уже создаёт там собственная поддержка SWC из @nestjs/swagger. Если тот плагин не зарегистрирован, файл останется пустым и никому не помешает.
Без typeCheck плагин просто замолкает. Сборка проходит, но docfy-metadata.json не записывается, и applyDocfyMetadata() нечего вливать. generate отслеживает именно это сочетание и предупреждает о нём.
Обнаружения DocfyModule в рантайме всё это не касается. Под SWC оно работает так же, как под tsc, потому что SWC по-прежнему компилирует по файлу на модуль и require.cache заполняется тем же порядком. Если включить typeCheck нельзя, связка @WithDocs() и DocfyModule.forRoot() решит задачу и вовсе без плагина.