mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(formatter): treat /*** comments as JSDoc (#27035)
As is well known, what counts as JSDoc depends on the consumer, no spec. e.g. Original JSDoc is strictly limited to `/**`, but TypeScript is not. Since this doesn't break JSDoc 3, it's more convenient for a formatter to take broader.
This commit is contained in:
1 parent
d77531504d
commit
3be5d94d2f
8 files changed
+85
-17
No files matched your search
@@ -661,3 +661,34 @@ var target = x ? y : /** @type {Document} */ ((root).head ?? fallback);
|
||||
A cast comment types the parenthesized expression directly after it.
|
||||
When the comment binds to an inner expression and the formatter adds parentheses around the whole (a `??` in a conditional branch, a sequence, a return argument), it prints inside the added pair so the cast keeps its target.
|
||||
Printed outside, the cast covers the whole expression and tsc types `root` as its uncast type again (`Property 'head' does not exist on type 'Node'`).
|
||||
|
||||
## triple-star-jsdoc-hard-break
|
||||
|
||||
- Why: uniform-rule (a block comment starting with `/**` is JSDoc)
|
||||
- Pin: `tests/fixtures/js/comments/jsdoc-trailing-double-space.js`
|
||||
|
||||
```js
|
||||
// input
|
||||
/***
|
||||
* a··
|
||||
* b
|
||||
*/
|
||||
|
||||
// ours
|
||||
/***
|
||||
* a··
|
||||
* b
|
||||
*/
|
||||
|
||||
// prettier
|
||||
/***
|
||||
* a
|
||||
* b
|
||||
*/
|
||||
```
|
||||
|
||||
`·` marks a trailing space.
|
||||
Prettier keeps the Markdown hard break in `/**` only, following jsdoc3.
|
||||
Tools disagree on `/***`: TypeScript (`isJSDocLikeText`, so editor hovers) treats it as JSDoc, jsdoc3 ignores it.
|
||||
Formatting it as JSDoc does not break jsdoc3, which ignores it either way.
|
||||
Type cast comments already follow the rule, in Prettier too.
|
||||
@@ -113,10 +113,12 @@
|
||||
//! ## References
|
||||
//! - [Prettier handles special comments](https://github.com/prettier/prettier/blob/7584432401a47a26943dd7a9ca9a8e032ead7285/src/language-js/comments/handle-comments.js)
|
||||
//! - [Prettier pre-processes comments](https://github.com/prettier/prettier/blob/7584432401a47a26943dd7a9ca9a8e032ead7285/src/main/comments/attach.js)
|
||||
use oxc_ast::{Comment, CommentContent};
|
||||
use oxc_ast::Comment;
|
||||
use oxc_formatter_core::SourceText;
|
||||
use oxc_span::{GetSpan, Span};
|
||||
|
||||
use super::trivia::is_jsdoc_comment;
|
||||
|
||||
/// Saved comment cursor state for [`Comments::snapshot`] / [`Comments::restore`].
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct CommentSnapshot {
|
||||
@@ -713,7 +715,7 @@ impl Comments<'_> {
|
||||
.is_some_and(|&byte| byte.is_ascii_whitespace() || byte == b'{')
|
||||
}
|
||||
|
||||
if !matches!(comment.content, CommentContent::Jsdoc) {
|
||||
if !is_jsdoc_comment(comment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -564,8 +564,7 @@ impl<'a> Format<'a, JsFormatContext<'a>> for FormatCommentText<'_> {
|
||||
let first_line = lines.next().unwrap();
|
||||
write!(f, [text(first_line.trim_end())]);
|
||||
|
||||
// Not `comment.is_jsdoc()`, which also accepts `/***`
|
||||
let is_jsdoc = content.starts_with("/**") && !content.starts_with("/***");
|
||||
let is_jsdoc = is_jsdoc_comment(comment);
|
||||
|
||||
// Indent the remaining lines by one space so that all `*` are aligned.
|
||||
for line in lines {
|
||||
@@ -659,3 +658,11 @@ fn is_alignable_comment(lines: &str) -> bool {
|
||||
pub fn is_alignable_block_comment(comment: &Comment, source_text: SourceText) -> bool {
|
||||
comment.is_multiline_block() && is_alignable_comment(source_text.text_for(&comment.span))
|
||||
}
|
||||
|
||||
/// A block comment starting with `/**`, including `/***`, at any position.
|
||||
///
|
||||
/// Not `Comment::is_jsdoc()`, which is leading only.
|
||||
/// For `/***`, see `DIVERGENCES.md#triple-star-jsdoc-hard-break`.
|
||||
pub fn is_jsdoc_comment(comment: &Comment) -> bool {
|
||||
matches!(comment.content, CommentContent::Jsdoc | CommentContent::JsdocLegal)
|
||||
}
|
||||
@@ -19,7 +19,10 @@ use crate::{
|
||||
formatter::{
|
||||
Comments, JsFormatter,
|
||||
prelude::*,
|
||||
trivia::{FormatLeadingComments, FormatTrailingComments, is_alignable_block_comment},
|
||||
trivia::{
|
||||
FormatLeadingComments, FormatTrailingComments, is_alignable_block_comment,
|
||||
is_jsdoc_comment,
|
||||
},
|
||||
},
|
||||
parentheses::NeedsParentheses,
|
||||
print::FormatWrite,
|
||||
@@ -302,7 +305,7 @@ impl LeadingCommentsInfo {
|
||||
info.has_line_ending_trailing_non_jsdoc_block_comment |= comment.is_block()
|
||||
&& comment.is_trailing()
|
||||
&& comment.followed_by_newline()
|
||||
&& !matches!(comment.content, CommentContent::Jsdoc | CommentContent::JsdocLegal);
|
||||
&& !is_jsdoc_comment(comment);
|
||||
info.has_line_ending_trailing_jsdoc_comment |=
|
||||
is_line_ending_trailing_jsdoc_comment(comment);
|
||||
}
|
||||
@@ -313,9 +316,7 @@ impl LeadingCommentsInfo {
|
||||
/// A trailing (same-line start) jsdoc comment that ends its source line;
|
||||
/// NOT own-line (that vocabulary means preceded by a newline, see `formatter/trivia.rs`)
|
||||
pub fn is_line_ending_trailing_jsdoc_comment(comment: &Comment) -> bool {
|
||||
matches!(comment.content, CommentContent::Jsdoc | CommentContent::JsdocLegal)
|
||||
&& comment.is_trailing()
|
||||
&& comment.followed_by_newline()
|
||||
is_jsdoc_comment(comment) && comment.is_trailing() && comment.followed_by_newline()
|
||||
}
|
||||
|
||||
/// Whether `ty` is a union that runs this printer.
|
||||
|
||||
+3
-2
@@ -35,10 +35,11 @@ function nested() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// DIVERGENCES.md#triple-star-jsdoc-hard-break
|
||||
/***
|
||||
* Not JSDoc, spaces go.
|
||||
* Also JSDoc, spaces stay.
|
||||
*/
|
||||
function notJsdoc() {}
|
||||
function tripleStar() {}
|
||||
|
||||
/*
|
||||
* Plain block comment, spaces go.
|
||||
|
||||
+9
-6
@@ -39,10 +39,11 @@ function nested() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// DIVERGENCES.md#triple-star-jsdoc-hard-break
|
||||
/***
|
||||
* Not JSDoc, spaces go.
|
||||
* Also JSDoc, spaces stay.
|
||||
*/
|
||||
function notJsdoc() {}
|
||||
function tripleStar() {}
|
||||
|
||||
/*
|
||||
* Plain block comment, spaces go.
|
||||
@@ -90,10 +91,11 @@ function nested() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// DIVERGENCES.md#triple-star-jsdoc-hard-break
|
||||
/***
|
||||
* Not JSDoc, spaces go.
|
||||
* Also JSDoc, spaces stay.
|
||||
*/
|
||||
function notJsdoc() {}
|
||||
function tripleStar() {}
|
||||
|
||||
/*
|
||||
* Plain block comment, spaces go.
|
||||
@@ -140,10 +142,11 @@ function nested() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// DIVERGENCES.md#triple-star-jsdoc-hard-break
|
||||
/***
|
||||
* Not JSDoc, spaces go.
|
||||
* Also JSDoc, spaces stay.
|
||||
*/
|
||||
function notJsdoc() {}
|
||||
function tripleStar() {}
|
||||
|
||||
/*
|
||||
* Plain block comment, spaces go.
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
// A legal JSDoc is a type cast too
|
||||
const x = /** @license @type {A} */ (y);
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
source: crates/oxc_formatter/tests/fixtures/mod.rs
|
||||
---
|
||||
==================== Input ====================
|
||||
// A legal JSDoc is a type cast too
|
||||
const x = /** @license @type {A} */ (y);
|
||||
|
||||
==================== Output ====================
|
||||
------------------
|
||||
{ printWidth: 80 }
|
||||
------------------
|
||||
// A legal JSDoc is a type cast too
|
||||
const x = /** @license @type {A} */ (y);
|
||||
|
||||
-------------------
|
||||
{ printWidth: 100 }
|
||||
-------------------
|
||||
// A legal JSDoc is a type cast too
|
||||
const x = /** @license @type {A} */ (y);
|
||||
|
||||
===================== End =====================
|
||||
Reference in new issue
Block a user