Skip to content

fix(plugin): emit @throws descriptions as proper string literals#3872

Merged
kamilmysliwiec merged 1 commit intonestjs:masterfrom
yogeshwaran-c:fix/plugin-throws-description-literal
Apr 22, 2026
Merged

fix(plugin): emit @throws descriptions as proper string literals#3872
kamilmysliwiec merged 1 commit intonestjs:masterfrom
yogeshwaran-c:fix/plugin-throws-description-literal

Conversation

@yogeshwaran-c
Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

Bug fix (plugin / TSDoc @throws handling).

What is the current behavior?

getTsDocErrorsOfNode stored the parsed @throws description pre-wrapped in double quotes and the caller fed that already-quoted text into factory.createNumericLiteral. That accidentally produced a valid-looking string literal for the common case, but broke as soon as the description contained a character that needs escaping inside a double-quoted string.

Minimal repro:

/**
 * create
 * @throws {400} foo \"bar\".
 */
@Post()
a() {}

Transpiles to:

openapi.ApiResponse({ status: 400, description: \"foo \"bar\".\" })

…which is a syntax error when the generated file is executed (the \"bar\" closes the string early). The same class of failure hits any @throws description with a \", a control character, or similar.

What is the new behavior?

  • getTsDocErrorsOfNode now returns the raw description text and the status as a number.
  • The plugin emits the description with factory.createStringLiteral, so the TypeScript printer escapes the text correctly.

After the fix the same input becomes:

openapi.ApiResponse({ status: 400, description: \"foo \\\"bar\\\".\" })

Regular descriptions (and numeric statuses) produce byte-identical output; the existing app.controller fixture is unchanged.

Additional context

Added a dedicated fixture test/plugin/fixtures/app.controller-throws-quotes.ts and a new test case in controller-class-visitor.spec.ts to cover the embedded-quote / multi-line @throws path.

getTsDocErrorsOfNode stored the parsed @throws description pre-wrapped
in double quotes and the caller then fed that already-quoted text into
factory.createNumericLiteral. That accidentally produced a valid-looking
string literal for the common case but fell apart as soon as the
description contained a character that needs escaping inside a
double-quoted string.

For example, a controller annotated as:

  /**
   * create
   * @throws {400} foo "bar".
   */
  @post()
  a() {}

transpiled to:

  openapi.ApiResponse({ status: 400, description: "foo "bar"." })

which is a syntax error when the generated file is later executed.

Store the raw description text and status number in the tag record, then
emit them with createStringLiteral / createNumericLiteral so the
TypeScript printer escapes them correctly. The transpiled output for
regular descriptions (and numeric statuses) is byte-identical, but
descriptions containing quotes, backslashes, and the like now round-trip
through the plugin cleanly.
@kamilmysliwiec kamilmysliwiec merged commit ed20cbf into nestjs:master Apr 22, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants