Il plugin CLI

La soluzione consigliata per webpack: true, automatica, a ogni build, senza uno step separato da ricordare.

È lo stesso meccanismo che il plugin CLI di @nestjs/swagger stesso usa per funzionare sotto webpack: true. Per questo @ApiProperty() non è richiesto su ogni proprietà DTO nemmeno in una build webpack. La Nest CLI passa un hook di plugin del compilatore TypeScript (compilerOptions.plugins) sia a tsc sia al builder webpack (ts-loader) in modo identico.

Registra il plugin

Registra nestjs-docfy in nest-cli.json:

nest-cli.json
{
  "compilerOptions": {
    "webpack": true,
    "plugins": ["nestjs-docfy"]
  }
}
main.ts
import { applyDocfyMetadata } from 'nestjs-docfy';

const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, applyDocfyMetadata(document));
OptionTypeDefaultDescription
metadataPathstringdocfy-metadata.json next to the running entry fileWhere to read the plugin-generated metadata from.
strictbooleanfalseThrow instead of warning when the metadata file is missing or invalid.

Rilevamento automatico

Dimenticare di registrare il plugin è facile da non notare finché una build non viene rilasciata senza documentazione, quindi ogni esecuzione di generate controlla già nest-cli.json e stampa un avviso quando webpack: true è impostato senza il plugin. Passa --register-plugin per farglielo correggere al posto tuo invece di modificare il file a mano:

bash
npx nestjs-docfy generate --register-plugin

Questo è opt-in di proposito, dato che alcuni team scelgono deliberatamente patch-spec invece di un plugin del compilatore, quindi generate non modifica mai nest-cli.json a meno che tu non lo chieda esplicitamente. Aggiunge un'entry a qualsiasi array compilerOptions.plugins esistente (riconoscendo sia le voci in forma stringa sia in forma oggetto come già registrate) e rispetta --dry-run.

Come funziona

Il plugin di nestjs-docfy non riscrive alcuna sintassi di decorator né tocca l'AST. A ogni compilazione riesegue esattamente la stessa analisi statica che generate/check/patch-spec già fanno (tramite ts-morph, contro l'albero sorgente, non il bundle) e scrive la patch risultante in docfy-metadata.json accanto all'output di build.

Poi, subito dopo SwaggerModule.createDocument(), applyDocfyMetadata() legge quel file e la unisce. Non c'è alcun coinvolgimento di require.cache né alcun problema di identità di classe bundlata, perché non ha mai bisogno dell'app in esecuzione per calcolare nulla.

vs. patch-spec

Manual, CI-driven alternative

Se preferisci non aggiungere un plugin del compilatore (ad esempio, una pipeline di build che non è la Nest CLI, o una policy più severa su cosa gira durante la compilazione), patch-spec calcola a mano la patch identica su un documento già costruito. Era l'unica opzione prima che esistesse il plugin, e resta utile per patch una tantum.

Builder SWC

@nestjs/cli tratta il builder SWC in modo diverso. Non chiama mai before() sotto "builder": "swc", solo un ReadonlyVisitor, e solo quando "typeCheck": true è impostato anche lui (altrimenti SWC salta del tutto il controllo dei tipi per conto suo). Questo plugin esporta entrambi gli hook, quindi attivare quell'opzione basta:

nest-cli.json
{
  "compilerOptions": {
    "builder": "swc",
    "typeCheck": true,
    "plugins": ["nestjs-docfy"]
  }
}
Requires typeCheck: true

Una cosa da sapere: registrare il plugin in questo modo scrive un file metadata.ts accanto alla radice del tuo source a ogni build. È lo stesso artefatto che il supporto SWC di @nestjs/swagger produce già lì, vuoto e innocuo se anche quel plugin non è registrato.

Ometti typeCheck e il plugin resta semplicemente in silenzio. Il build continua a riuscire, ma nessun docfy-metadata.json viene scritto e applyDocfyMetadata() non ha nulla da unire. generate intercetta esattamente questa combinazione e avvisa.

Niente di tutto questo tocca la discovery a runtime di DocfyModule. Funziona sotto SWC esattamente come sotto tsc, dato che SWC continua a compilare un file per modulo e require.cache finisce popolato allo stesso modo. Se impostare typeCheck non è un'opzione, @WithDocs() insieme a DocfyModule.forRoot() ti ci porta comunque, senza il plugin.