Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 54 additions & 3 deletions crates/oxc_codegen/src/comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,58 @@ use crate::{Codegen, LegalComment, options::CommentOptions};

pub type CommentsMap = FxHashMap</* attached_to */ u32, Vec<Comment>>;

/// Custom iterator that splits text on line terminators while handling CRLF as a single unit.
/// This avoids creating empty strings between CR and LF characters.
///
/// # Example
/// Standard split would turn `"line1\r\nline2"` into `["line1", "", "line2"]` because
/// it treats \r and \n as separate terminators. This iterator correctly produces
/// `["line1", "line2"]` by treating \r\n as a single terminator.
struct LineTerminatorSplitter<'a> {
text: &'a str,
position: usize,
}

impl<'a> LineTerminatorSplitter<'a> {
fn new(text: &'a str) -> Self {
Self { text, position: 0 }
}
}

impl<'a> Iterator for LineTerminatorSplitter<'a> {
type Item = &'a str;

fn next(&mut self) -> Option<Self::Item> {
if self.position >= self.text.len() {
return None;
}

let start = self.position;
let chars = self.text[self.position..].char_indices();

for (i, c) in chars {
if is_line_terminator(c) {
let line = &self.text[start..start + i];
self.position = start + i + c.len_utf8();

// If this is CR followed by LF, skip the LF to treat CRLF as a single terminator
if c == '\r'
&& self.text.as_bytes().get(self.position).is_some_and(|&next| next == b'\n')
{
self.position += 1;
}

return Some(line);
}
}

// Return the remaining text
let line = &self.text[start..];
self.position = self.text.len();
Some(line)
}
}

impl Codegen<'_> {
pub(crate) fn build_comments(&mut self, comments: &[Comment]) {
if self.options.comments == CommentOptions::disabled() {
Expand Down Expand Up @@ -132,8 +184,7 @@ impl Codegen<'_> {
self.print_str_escaping_script_close_tag(comment_source);
}
CommentKind::Block => {
// Print block comments with our own indentation.
for line in comment_source.split(is_line_terminator) {
for line in LineTerminatorSplitter::new(comment_source) {
if !line.starts_with("/*") {
self.print_indent();
}
Expand Down Expand Up @@ -167,7 +218,7 @@ impl Codegen<'_> {
if comment.is_block() && text.contains(is_line_terminator) {
let mut buffer = String::with_capacity(text.len());
// Print block comments with our own indentation.
for line in text.split(is_line_terminator) {
for line in LineTerminatorSplitter::new(&text) {
if !line.starts_with("/*") {
buffer.push('\t');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
declare const decorator: any;

export class Test1 {
/** This method will trigger the feature highlight dialog load/show based on dialogId and analyticsId */
/**
* This method will trigger the feature highlight dialog load/show based on dialogId and analyticsId
*/
@decorator
property: (() => any) | undefined;
}

export class Test2 {
/** This method will trigger the feature highlight dialog load/show based on dialogId and analyticsI */
/**
* This method will trigger the feature highlight dialog load/show based on dialogId and analyticsI
*/
@decorator
property: ((arg: any) => any) | undefined;
}
Expand Down
26 changes: 19 additions & 7 deletions crates/oxc_isolated_declarations/tests/fixtures/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,29 @@ export abstract class Qux {
}

export class Baz {
/** Just a comment */
/**
* Just a comment
*/
readonly prop1 = "some string";
/** Just a comment */
/**
* Just a comment
*/
prop2 = "another string";
/** Just a comment */
/**
* Just a comment
*/
private prop3 = "yet another string";
/** Just a comment */
/**
* Just a comment
*/
private prop4(): void {}
/** Just a comment */
/**
* Just a comment
*/
private static prop5 = "yet another string";
/** Just a comment */
/**
* Just a comment
*/
private static prop6(): void {}
}

Expand Down Expand Up @@ -142,4 +154,4 @@ export class PrivateConstructorWithDefaultParameters {
readonly prop3: boolean = true,
normalParam: string = "normal",
) {}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const defaultDelimitersClose = new Uint8Array([125, 125]);

/** comment should be a leading comment of the class */
/**
* comment should be a leading comment of the class
*/
export default class Tokenizer {
public delimiterClose: Uint8Array = defaultDelimitersClose;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/** comment should be a leading comment of the arrow function */
/**
* comment should be a leading comment of the arrow function
*/
export default () => {
return 0;
};
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ export interface StripInternalInterfaceSignatures {
* @internal
*/
internalProperty: number;
/**@internal */
/**
* @internal
*/
new (): any;
}

Expand All @@ -63,7 +65,9 @@ export type StripInternalTypeSignatures = {
* @internal
*/
internalProperty: number;
/**@internal */
/**
* @internal
*/
new (): any;
};

Expand All @@ -79,4 +83,4 @@ export namespace StripInternalNamespaceInner {
/**
* @internal
*/
export namespace StripInternalNamespace {}
export namespace StripInternalNamespace {}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const Res = 0;

/** comment should be a leading comment of the function Foo */
/**
* comment should be a leading comment of the function Foo
*/
export = function Foo(): typeof Res {
return Res;
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ input_file: crates/oxc_isolated_declarations/tests/fixtures/class-decorator.ts
==================== .D.TS ====================

export declare class Test1 {
/** This method will trigger the feature highlight dialog load/show based on dialogId and analyticsId */
/**
* This method will trigger the feature highlight dialog load/show based on dialogId and analyticsId
*/
property: (() => any) | undefined;
}
export declare class Test2 {
/** This method will trigger the feature highlight dialog load/show based on dialogId and analyticsI */
/**
* This method will trigger the feature highlight dialog load/show based on dialogId and analyticsI
*/
property: ((arg: any) => any) | undefined;
}
export declare class Test3 {
Expand Down
40 changes: 26 additions & 14 deletions crates/oxc_isolated_declarations/tests/snapshots/class.snap
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,29 @@ export declare abstract class Qux {
baz(): void;
}
export declare class Baz {
/** Just a comment */
/**
* Just a comment
*/
readonly prop1 = "some string";
/** Just a comment */
/**
* Just a comment
*/
prop2: string;
/** Just a comment */
/**
* Just a comment
*/
private prop3;
/** Just a comment */
/**
* Just a comment
*/
private prop4;
/** Just a comment */
/**
* Just a comment
*/
private static prop5;
/** Just a comment */
/**
* Just a comment
*/
private static prop6;
}
export declare class Boo {
Expand Down Expand Up @@ -114,20 +126,20 @@ export declare class PrivateConstructorWithDefaultParameters {

x TS9038: Computed property names on class or object literals cannot be
| inferred with --isolatedDeclarations.
,-[69:14]
68 | public get badGetter() {
69 | return {[('x')]: 1};
,-[81:14]
80 | public get badGetter() {
81 | return {[('x')]: 1};
: ^^^^^
70 | }
82 | }
`----

x TS9011: Parameter must have an explicit type annotation with
| --isolatedDeclarations.
,-[67:14]
66 | export class PublicMethodClass {
67 | public bad(a): void {}
,-[79:14]
78 | export class PublicMethodClass {
79 | public bad(a): void {}
: ^
68 | public get badGetter() {
80 | public get badGetter() {
`----


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ input_file: crates/oxc_isolated_declarations/tests/fixtures/export-default.ts
```
==================== .D.TS ====================

/** comment should be a leading comment of the class */
/**
* comment should be a leading comment of the class
*/
export default class Tokenizer {
delimiterClose: Uint8Array;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ input_file: crates/oxc_isolated_declarations/tests/fixtures/export-default2.ts
```
==================== .D.TS ====================

/** comment should be a leading comment of the arrow function */
/**
* comment should be a leading comment of the arrow function
*/
declare const _default: () => number;
export default _default;
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ input_file: crates/oxc_isolated_declarations/tests/fixtures/ts-export-assignment
==================== .D.TS ====================

declare const Res = 0;
/** comment should be a leading comment of the function Foo */
/**
* comment should be a leading comment of the function Foo
*/
declare const _default: () => typeof Res;
export = _default;
Loading