swc_fast_ts_strip/
lib.rs

1use std::{cell::RefCell, fmt::Display, rc::Rc};
2
3use anyhow::Context;
4use bytes_str::BytesStr;
5use serde::{Deserialize, Serialize};
6use swc_common::{
7    comments::SingleThreadedComments,
8    errors::{DiagnosticId, Handler, HANDLER},
9    source_map::DefaultSourceMapGenConfig,
10    sync::Lrc,
11    BytePos, FileName, Mark, SourceMap, Span, Spanned,
12};
13use swc_ecma_ast::{
14    ArrayPat, ArrowExpr, AutoAccessor, BindingIdent, Class, ClassDecl, ClassMethod, ClassProp,
15    Constructor, Decl, DefaultDecl, DoWhileStmt, EsVersion, ExportAll, ExportDecl,
16    ExportDefaultDecl, ExportSpecifier, FnDecl, ForInStmt, ForOfStmt, ForStmt, GetterProp, IfStmt,
17    ImportDecl, ImportSpecifier, ModuleDecl, ModuleItem, NamedExport, ObjectPat, Param, Pat,
18    PrivateMethod, PrivateProp, Program, ReturnStmt, SetterProp, Stmt, ThrowStmt, TsAsExpr,
19    TsConstAssertion, TsEnumDecl, TsExportAssignment, TsImportEqualsDecl, TsIndexSignature,
20    TsInstantiation, TsModuleDecl, TsModuleName, TsNamespaceBody, TsNonNullExpr, TsParamPropParam,
21    TsSatisfiesExpr, TsTypeAliasDecl, TsTypeAnn, TsTypeAssertion, TsTypeParamDecl,
22    TsTypeParamInstantiation, VarDeclarator, WhileStmt, YieldExpr,
23};
24use swc_ecma_lexer::{
25    lexer::Lexer,
26    token::{BinOpToken, IdentLike, KnownIdent, Token, TokenAndSpan, Word},
27    Capturing, Parser, StringInput, Syntax, TsSyntax,
28};
29use swc_ecma_transforms_base::{
30    fixer::fixer,
31    helpers::{inject_helpers, Helpers, HELPERS},
32    hygiene::hygiene,
33    resolver,
34};
35use swc_ecma_transforms_typescript::typescript;
36use swc_ecma_visit::{Visit, VisitWith};
37#[cfg(feature = "wasm-bindgen")]
38use wasm_bindgen::prelude::*;
39
40#[derive(Default, Deserialize)]
41#[serde(rename_all = "camelCase")]
42pub struct Options {
43    #[serde(default)]
44    pub module: Option<bool>,
45    #[serde(default)]
46    pub filename: Option<String>,
47
48    #[serde(default = "default_ts_syntax")]
49    pub parser: TsSyntax,
50
51    #[serde(default)]
52    pub mode: Mode,
53
54    #[serde(default)]
55    pub transform: Option<TransformConfig>,
56
57    #[serde(default)]
58    pub deprecated_ts_module_as_error: Option<bool>,
59
60    #[serde(default)]
61    pub source_map: bool,
62}
63
64#[derive(Debug, Default, Deserialize)]
65#[serde(rename_all = "camelCase")]
66pub struct TransformConfig {
67    #[cfg(feature = "nightly")]
68    #[serde(default)]
69    pub jsx: Option<JsxConfig>,
70
71    #[serde(flatten)]
72    pub typescript: typescript::Config,
73}
74
75#[cfg(feature = "nightly")]
76#[derive(Debug, Default, Deserialize)]
77#[serde(rename_all = "camelCase")]
78pub struct JsxConfig {
79    #[serde(default)]
80    pub transform: Option<JsxTransform>,
81
82    #[serde(default)]
83    pub import_source: Option<swc_atoms::Atom>,
84}
85
86#[cfg(feature = "nightly")]
87#[derive(Debug, Default, Deserialize)]
88#[serde(rename_all = "camelCase")]
89pub enum JsxTransform {
90    #[default]
91    #[serde(rename = "react-jsx")]
92    ReactJsx,
93    #[serde(rename = "react-jsxdev")]
94    ReactJsxDev,
95}
96
97#[cfg(feature = "wasm-bindgen")]
98#[wasm_bindgen(typescript_custom_section)]
99const Type_Options: &'static str = r#"
100interface Options {
101    module?: boolean;
102    filename?: string;
103    mode?: Mode;
104    transform?: TransformConfig;
105    deprecatedTsModuleAsError?: boolean;
106    sourceMap?: boolean;
107}
108
109interface TransformConfig {
110    /**
111     * @see https://www.typescriptlang.org/tsconfig#verbatimModuleSyntax
112     */
113    verbatimModuleSyntax?: boolean;
114    /**
115     * Native class properties support
116     */
117    nativeClassProperties?: boolean;
118    importNotUsedAsValues?: "remove" | "preserve";
119    /**
120     * Don't create `export {}`.
121     * By default, strip creates `export {}` for modules to preserve module
122     * context.
123     * 
124     * @see https://github.com/swc-project/swc/issues/1698
125     */
126    noEmptyExport?: boolean;
127    importExportAssignConfig?: "Classic" | "Preserve" | "NodeNext" | "EsNext";
128    /**
129     * Disables an optimization that inlines TS enum member values
130     * within the same module that assumes the enum member values
131     * are never modified.
132     * 
133     * Defaults to false.
134     */
135    tsEnumIsMutable?: boolean;
136
137    /**
138     * Available only on nightly builds.
139     */
140    jsx?: JsxConfig;
141}
142
143interface JsxConfig {
144    /**
145     * How to transform JSX.
146     * 
147     * @default "react-jsx"
148     */
149    transform?: "react-jsx" | "react-jsxdev";
150}
151"#;
152
153#[derive(Debug, Default, Deserialize)]
154#[serde(rename_all = "kebab-case")]
155pub enum Mode {
156    #[default]
157    StripOnly,
158    Transform,
159}
160
161#[cfg(feature = "wasm-bindgen")]
162#[wasm_bindgen(typescript_custom_section)]
163const Type_Mode: &'static str = r#"
164type Mode = "strip-only" | "transform";
165"#;
166
167fn default_ts_syntax() -> TsSyntax {
168    TsSyntax {
169        decorators: true,
170        ..Default::default()
171    }
172}
173
174#[derive(Debug, Serialize)]
175pub struct TransformOutput {
176    pub code: String,
177    pub map: Option<String>,
178}
179
180#[cfg(feature = "wasm-bindgen")]
181#[wasm_bindgen(typescript_custom_section)]
182const Type_TransformOutput: &'static str = r#"
183interface TransformOutput {
184    code: string;
185    map?: string;
186}
187"#;
188
189#[derive(Debug, Serialize)]
190pub struct TsError {
191    pub message: String,
192    pub code: ErrorCode,
193}
194
195impl std::fmt::Display for TsError {
196    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
197        write!(f, "[{}] {}", self.code, self.message)
198    }
199}
200
201impl std::error::Error for TsError {
202    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
203        None
204    }
205}
206
207#[non_exhaustive]
208#[derive(Debug, Clone, Copy, Serialize)]
209pub enum ErrorCode {
210    InvalidSyntax,
211    UnsupportedSyntax,
212    Unknown,
213}
214
215impl Display for ErrorCode {
216    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217        write!(f, "{self:?}")
218    }
219}
220
221impl From<anyhow::Error> for TsError {
222    fn from(err: anyhow::Error) -> Self {
223        TsError {
224            message: err.to_string(),
225            code: ErrorCode::Unknown,
226        }
227    }
228}
229
230pub fn operate(
231    cm: &Lrc<SourceMap>,
232    handler: &Handler,
233    input: String,
234    options: Options,
235) -> Result<TransformOutput, TsError> {
236    let filename = options
237        .filename
238        .map_or(FileName::Anon, |f| FileName::Real(f.into()));
239
240    let source_len = input.len();
241    let fm = cm.new_source_file(filename.into(), input);
242
243    let syntax = Syntax::Typescript(options.parser);
244    let target = EsVersion::latest();
245
246    let comments = SingleThreadedComments::default();
247
248    let lexer = Capturing::new(Lexer::new(
249        syntax,
250        target,
251        StringInput::from(&*fm),
252        Some(&comments),
253    ));
254    let tokens = lexer.tokens().clone();
255
256    let mut parser = Parser::new_from(lexer);
257
258    let program = match options.module {
259        Some(true) => parser.parse_module().map(Program::Module),
260        Some(false) => parser.parse_script().map(Program::Script),
261        None => parser.parse_program(),
262    };
263    let errors = parser.take_errors();
264
265    let mut program = match program {
266        Ok(program) => program,
267        Err(err) => {
268            err.into_diagnostic(handler)
269                .code(DiagnosticId::Error("InvalidSyntax".into()))
270                .emit();
271
272            for e in errors {
273                e.into_diagnostic(handler)
274                    .code(DiagnosticId::Error("InvalidSyntax".into()))
275                    .emit();
276            }
277
278            return Err(TsError {
279                message: "Syntax error".to_string(),
280                code: ErrorCode::InvalidSyntax,
281            });
282        }
283    };
284
285    if !errors.is_empty() {
286        for e in errors {
287            e.into_diagnostic(handler)
288                .code(DiagnosticId::Error("InvalidSyntax".into()))
289                .emit();
290        }
291
292        return Err(TsError {
293            message: "Syntax error".to_string(),
294            code: ErrorCode::InvalidSyntax,
295        });
296    }
297
298    drop(parser);
299
300    let deprecated_ts_module_as_error = options.deprecated_ts_module_as_error.unwrap_or_default();
301
302    match options.mode {
303        Mode::StripOnly => {
304            let mut tokens = RefCell::into_inner(Rc::try_unwrap(tokens).unwrap());
305
306            tokens.sort_by_key(|t| t.span);
307
308            if deprecated_ts_module_as_error {
309                program.visit_with(&mut ErrorOnTsModule {
310                    src: &fm.src,
311                    tokens: &tokens,
312                });
313                if handler.has_errors() {
314                    return Err(TsError {
315                        message: "Unsupported syntax".to_string(),
316                        code: ErrorCode::UnsupportedSyntax,
317                    });
318                }
319            }
320
321            // Strip typescript types
322            let mut ts_strip = TsStrip::new(fm.src.clone(), tokens);
323
324            program.visit_with(&mut ts_strip);
325            if handler.has_errors() {
326                return Err(TsError {
327                    message: "Unsupported syntax".to_string(),
328                    code: ErrorCode::UnsupportedSyntax,
329                });
330            }
331
332            let replacements = ts_strip.replacements;
333            let overwrites = ts_strip.overwrites;
334
335            if replacements.is_empty() && overwrites.is_empty() {
336                return Ok(TransformOutput {
337                    code: fm.src.to_string(),
338                    map: Default::default(),
339                });
340            }
341
342            let source = fm.src.clone();
343            let mut code = fm.src.to_string().into_bytes();
344
345            for r in replacements {
346                let (start, end) = (r.0 .0 as usize - 1, r.1 .0 as usize - 1);
347
348                for (i, c) in source[start..end].char_indices() {
349                    let i = start + i;
350                    match c {
351                        // https://262.ecma-international.org/#sec-white-space
352                        '\u{0009}' | '\u{0000B}' | '\u{000C}' | '\u{FEFF}' => continue,
353                        // Space_Separator
354                        '\u{0020}' | '\u{00A0}' | '\u{1680}' | '\u{2000}' | '\u{2001}'
355                        | '\u{2002}' | '\u{2003}' | '\u{2004}' | '\u{2005}' | '\u{2006}'
356                        | '\u{2007}' | '\u{2008}' | '\u{2009}' | '\u{200A}' | '\u{202F}'
357                        | '\u{205F}' | '\u{3000}' => continue,
358                        // https://262.ecma-international.org/#sec-line-terminators
359                        '\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}' => continue,
360                        _ => match c.len_utf8() {
361                            1 => {
362                                // Space 0020
363                                code[i] = 0x20;
364                            }
365                            2 => {
366                                // No-Break Space 00A0
367                                code[i] = 0xc2;
368                                code[i + 1] = 0xa0;
369                            }
370                            3 => {
371                                // En Space 2002
372                                code[i] = 0xe2;
373                                code[i + 1] = 0x80;
374                                code[i + 2] = 0x82;
375                            }
376                            4 => {
377                                // We do not have a 4-byte space character in the Unicode standard.
378
379                                // Space 0020
380                                code[i] = 0x20;
381                                // ZWNBSP FEFF
382                                code[i + 1] = 0xef;
383                                code[i + 2] = 0xbb;
384                                code[i + 3] = 0xbf;
385                            }
386                            _ => unreachable!(),
387                        },
388                    }
389                }
390            }
391
392            for (i, v) in overwrites {
393                code[i.0 as usize - 1] = v;
394            }
395
396            let code = if cfg!(debug_assertions) {
397                String::from_utf8(code).map_err(|err| TsError {
398                    message: format!("failed to convert to utf-8: {err}"),
399                    code: ErrorCode::Unknown,
400                })?
401            } else {
402                // SAFETY: We've already validated that the source is valid utf-8
403                // and our operations are limited to character-level string replacements.
404                unsafe { String::from_utf8_unchecked(code) }
405            };
406
407            Ok(TransformOutput {
408                code,
409                map: Default::default(),
410            })
411        }
412
413        Mode::Transform => {
414            let unresolved_mark = Mark::new();
415            let top_level_mark = Mark::new();
416
417            HELPERS.set(&Helpers::new(false), || {
418                program.mutate(&mut resolver(unresolved_mark, top_level_mark, true));
419
420                if deprecated_ts_module_as_error {
421                    let mut tokens = RefCell::into_inner(Rc::try_unwrap(tokens).unwrap());
422
423                    tokens.sort_by_key(|t| t.span);
424
425                    program.visit_with(&mut ErrorOnTsModule {
426                        src: &fm.src,
427                        tokens: &tokens,
428                    });
429                    if handler.has_errors() {
430                        return Err(TsError {
431                            message: "Unsupported syntax".to_string(),
432                            code: ErrorCode::UnsupportedSyntax,
433                        });
434                    }
435                }
436
437                let transform = options.transform.unwrap_or_default();
438
439                program.mutate(&mut typescript::typescript(
440                    transform.typescript,
441                    unresolved_mark,
442                    top_level_mark,
443                ));
444
445                #[cfg(feature = "nightly")]
446                program.mutate(&mut swc_ecma_transforms_react::jsx(
447                    cm.clone(),
448                    Some(comments.clone()),
449                    swc_ecma_transforms_react::Options {
450                        next: Some(true),
451                        runtime: Some(swc_ecma_transforms_react::Runtime::Automatic),
452                        import_source: transform
453                            .jsx
454                            .as_ref()
455                            .and_then(|jsx| jsx.import_source.clone()),
456                        development: match transform.jsx {
457                            Some(JsxConfig {
458                                transform: Some(transform),
459                                ..
460                            }) => Some(matches!(transform, JsxTransform::ReactJsxDev)),
461                            _ => None,
462                        },
463                        refresh: None,
464                        ..Default::default()
465                    },
466                    top_level_mark,
467                    unresolved_mark,
468                ));
469
470                program.mutate(&mut inject_helpers(unresolved_mark));
471
472                program.mutate(&mut hygiene());
473
474                program.mutate(&mut fixer(Some(&comments)));
475
476                Ok(())
477            })?;
478
479            let mut src = std::vec::Vec::with_capacity(source_len);
480            let mut src_map_buf = if options.source_map {
481                Some(Vec::new())
482            } else {
483                None
484            };
485
486            {
487                let mut emitter = swc_ecma_codegen::Emitter {
488                    cfg: swc_ecma_codegen::Config::default(),
489                    comments: if options.source_map {
490                        Some(&comments)
491                    } else {
492                        None
493                    },
494                    cm: cm.clone(),
495                    wr: swc_ecma_codegen::text_writer::JsWriter::new(
496                        cm.clone(),
497                        "\n",
498                        &mut src,
499                        src_map_buf.as_mut(),
500                    ),
501                };
502
503                emitter.emit_program(&program).unwrap();
504
505                let map = src_map_buf
506                    .map(|map| {
507                        let map = cm.build_source_map(&map, None, DefaultSourceMapGenConfig);
508
509                        let mut s = std::vec::Vec::new();
510                        map.to_writer(&mut s)
511                            .context("failed to write source map")?;
512
513                        String::from_utf8(s).context("source map was not utf8")
514                    })
515                    .transpose()?;
516
517                Ok(TransformOutput {
518                    code: String::from_utf8(src).context("generated code was not utf-8")?,
519                    map,
520                })
521            }
522        }
523    }
524}
525
526struct ErrorOnTsModule<'a> {
527    src: &'a str,
528    tokens: &'a [TokenAndSpan],
529}
530
531// All namespaces or modules are either at the top level or nested within
532// another namespace or module.
533impl Visit for ErrorOnTsModule<'_> {
534    fn visit_stmt(&mut self, n: &Stmt) {
535        if n.is_decl() {
536            n.visit_children_with(self);
537        }
538    }
539
540    fn visit_decl(&mut self, n: &Decl) {
541        if n.is_ts_module() {
542            n.visit_children_with(self);
543        }
544    }
545
546    fn visit_module_decl(&mut self, n: &ModuleDecl) {
547        if n.is_export_decl() {
548            n.visit_children_with(self);
549        }
550    }
551
552    fn visit_ts_module_decl(&mut self, n: &TsModuleDecl) {
553        n.visit_children_with(self);
554
555        if n.global || n.id.is_str() {
556            return;
557        }
558
559        let mut pos = n.span.lo;
560
561        if n.declare {
562            let declare_index = self
563                .tokens
564                .binary_search_by_key(&pos, |t| t.span.lo)
565                .unwrap();
566
567            debug_assert_eq!(
568                self.tokens[declare_index].token,
569                Token::Word(Word::Ident(IdentLike::Known(KnownIdent::Declare)))
570            );
571
572            let TokenAndSpan { token, span, .. } = &self.tokens[declare_index + 1];
573            // declare global
574            // declare module
575            // declare namespace
576            if let Token::Word(Word::Ident(IdentLike::Known(KnownIdent::Namespace))) = token {
577                return;
578            }
579
580            pos = span.lo;
581        } else if self.src.as_bytes()[pos.0 as usize - 1] != b'm' {
582            return;
583        }
584
585        HANDLER.with(|handler| {
586            handler
587                .struct_span_err(
588                    span(pos, n.id.span().hi),
589                    "`module` keyword is not supported. Use `namespace` instead.",
590                )
591                .code(DiagnosticId::Error("UnsupportedSyntax".into()))
592                .emit();
593        });
594    }
595}
596
597struct TsStrip {
598    src: BytesStr,
599
600    /// Replaced with whitespace
601    replacements: Vec<(BytePos, BytePos)>,
602
603    // should be string, but we use u8 for only `)` usage.
604    overwrites: Vec<(BytePos, u8)>,
605
606    tokens: std::vec::Vec<TokenAndSpan>,
607}
608
609impl TsStrip {
610    fn new(src: BytesStr, tokens: std::vec::Vec<TokenAndSpan>) -> Self {
611        TsStrip {
612            src,
613            replacements: Default::default(),
614            overwrites: Default::default(),
615            tokens,
616        }
617    }
618}
619
620impl TsStrip {
621    fn add_replacement(&mut self, span: Span) {
622        self.replacements.push((span.lo, span.hi));
623    }
624
625    fn add_overwrite(&mut self, pos: BytePos, value: u8) {
626        self.overwrites.push((pos, value));
627    }
628
629    fn get_src_slice(&self, span: Span) -> &str {
630        &self.src[(span.lo.0 - 1) as usize..(span.hi.0 - 1) as usize]
631    }
632
633    fn get_next_token_index(&self, pos: BytePos) -> usize {
634        let index = self.tokens.binary_search_by_key(&pos, |t| t.span.lo);
635        match index {
636            Ok(index) => index,
637            Err(index) => index,
638        }
639    }
640
641    fn get_next_token(&self, pos: BytePos) -> &TokenAndSpan {
642        &self.tokens[self.get_next_token_index(pos)]
643    }
644
645    fn get_prev_token_index(&self, pos: BytePos) -> usize {
646        let index = self.tokens.binary_search_by_key(&pos, |t| t.span.lo);
647        match index {
648            Ok(index) => index,
649            Err(index) => index - 1,
650        }
651    }
652
653    fn get_prev_token(&self, pos: BytePos) -> &TokenAndSpan {
654        &self.tokens[self.get_prev_token_index(pos)]
655    }
656
657    fn fix_asi(&mut self, span: Span) {
658        let index = self.get_prev_token_index(span.lo);
659        if index == 0 {
660            // Skip if the token is the first token.
661            return;
662        }
663
664        let TokenAndSpan {
665            token: prev_token,
666            span: prev_span,
667            ..
668        } = &self.tokens[index - 1];
669
670        let index = self.get_prev_token_index(span.hi - BytePos(1));
671        if index == self.tokens.len() - 1 {
672            // Skip if the token is the last token.
673            return;
674        }
675
676        let TokenAndSpan {
677            token,
678            had_line_break,
679            ..
680        } = &self.tokens[index + 1];
681
682        if !*had_line_break {
683            return;
684        }
685
686        // https://tc39.es/ecma262/multipage/ecmascript-language-lexical-grammar.html#sec-asi-interesting-cases-in-statement-lists
687        // Add a semicolon if the next token is `[`, `(`, `/`, `+`, `-` or backtick.
688        match token {
689            Token::LParen
690            | Token::LBracket
691            | Token::BackQuote
692            | Token::BinOp(BinOpToken::Add | BinOpToken::Sub | BinOpToken::Div) => {
693                if prev_token == &Token::Semi {
694                    self.add_overwrite(prev_span.lo, b';');
695                    return;
696                }
697
698                self.add_overwrite(span.lo, b';');
699            }
700
701            _ => {}
702        }
703    }
704
705    fn fix_asi_in_expr(&mut self, span: Span) {
706        let index = self.get_prev_token_index(span.hi - BytePos(1));
707        if index == self.tokens.len() - 1 {
708            return;
709        }
710
711        if let TokenAndSpan {
712            // Only `(`, `[` and backtick affect ASI.
713            token: Token::LParen | Token::LBracket | Token::BackQuote,
714            had_line_break: true,
715            ..
716        } = &self.tokens[index + 1]
717        {
718            self.add_overwrite(span.lo, b';');
719        }
720    }
721
722    fn strip_class_modifier(&mut self, mut start_pos: BytePos, key_pos: BytePos) {
723        let mut index = self.get_next_token_index(start_pos);
724
725        while start_pos < key_pos {
726            let TokenAndSpan { token, span, .. } = &self.tokens[index];
727            start_pos = span.hi;
728            index += 1;
729
730            let next = &self.tokens[index];
731
732            if next.had_line_break {
733                return;
734            }
735
736            // see ts_next_token_can_follow_modifier
737            // class { public public() {} }
738            if !matches!(
739                next.token,
740                Token::LBracket
741                    | Token::LBrace
742                    | Token::BinOp(BinOpToken::Mul)
743                    | Token::DotDotDot
744                    | Token::Hash
745                    | Token::Word(_)
746                    | Token::Str { .. }
747                    | Token::Num { .. }
748                    | Token::BigInt { .. }
749            ) {
750                return;
751            }
752
753            match token {
754                Token::Word(Word::Ident(IdentLike::Known(KnownIdent::Static))) => {
755                    continue;
756                }
757                Token::Word(Word::Ident(IdentLike::Known(
758                    KnownIdent::Readonly
759                    | KnownIdent::Public
760                    | KnownIdent::Protected
761                    | KnownIdent::Private,
762                ))) => {
763                    self.add_replacement(*span);
764                }
765                Token::Word(Word::Ident(IdentLike::Other(o))) if *o == "override" => {
766                    self.add_replacement(*span);
767                }
768                _ => {
769                    return;
770                }
771            }
772        }
773    }
774
775    fn strip_definite_mark(&mut self, index: usize) {
776        self.strip_token(index, Token::Bang);
777    }
778
779    fn strip_optional_mark(&mut self, index: usize) {
780        self.strip_token(index, Token::QuestionMark);
781    }
782
783    fn strip_token(&mut self, index: usize, expected: Token) {
784        let TokenAndSpan { token, span, .. } = &self.tokens[index];
785        debug_assert_eq!(*token, expected);
786
787        self.add_replacement(*span);
788    }
789
790    // ```TypeScript
791    // return/yield/throw <T>
792    //     (v: T) => v;
793    // ```
794    //
795    // ```TypeScript
796    // return/yield/throw (
797    //      v   ) => v;
798    // ```
799    fn fix_asi_in_arrow_expr(&mut self, arrow_expr: &ArrowExpr) {
800        if let Some(tp) = &arrow_expr.type_params {
801            let l_paren = self.get_next_token(tp.span.hi);
802            debug_assert_eq!(l_paren.token, Token::LParen);
803
804            let slice = self.get_src_slice(tp.span.with_hi(l_paren.span.lo));
805
806            if !slice.chars().any(is_new_line) {
807                return;
808            }
809
810            let l_paren_pos = l_paren.span.lo;
811            let l_lt_pos = tp.span.lo;
812
813            self.add_overwrite(l_paren_pos, b' ');
814            self.add_overwrite(l_lt_pos, b'(');
815        }
816    }
817}
818
819impl Visit for TsStrip {
820    fn visit_var_declarator(&mut self, n: &VarDeclarator) {
821        if n.definite {
822            if let Some(id) = n.name.as_ident() {
823                let mark_index = self.get_next_token_index(id.span.hi);
824                self.strip_definite_mark(mark_index);
825            };
826        }
827
828        n.visit_children_with(self);
829    }
830
831    fn visit_arrow_expr(&mut self, n: &ArrowExpr) {
832        'type_params: {
833            // ```TypeScript
834            // let f = async <
835            //    T
836            // >(v: T) => v;
837            // ```
838
839            // ```TypeScript
840            // let f = async (
841            //
842            //   v   ) => v;
843            // ```
844            if let Some(tp) = &n.type_params {
845                self.add_replacement(tp.span);
846
847                if !n.is_async {
848                    break 'type_params;
849                }
850
851                let slice = self.get_src_slice(tp.span);
852                if !slice.chars().any(is_new_line) {
853                    break 'type_params;
854                }
855
856                let l_paren = self.get_next_token(tp.span.hi);
857                debug_assert_eq!(l_paren.token, Token::LParen);
858                let l_paren_pos = l_paren.span.lo;
859                let l_lt_pos = tp.span.lo;
860
861                self.add_overwrite(l_paren_pos, b' ');
862                self.add_overwrite(l_lt_pos, b'(');
863            }
864        }
865
866        if let Some(ret) = &n.return_type {
867            self.add_replacement(ret.span);
868
869            let r_paren = self.get_prev_token(ret.span.lo - BytePos(1));
870            debug_assert_eq!(r_paren.token, Token::RParen);
871            let arrow = self.get_next_token(ret.span.hi);
872            debug_assert_eq!(arrow.token, Token::Arrow);
873            let span = span(r_paren.span.lo, arrow.span.lo);
874
875            let slice = self.get_src_slice(span);
876            if slice.chars().any(is_new_line) {
877                self.add_replacement(r_paren.span);
878
879                // Instead of moving the arrow mark, we shift the right parenthesis to the next
880                // line. This is because there might be a line break after the right
881                // parenthesis, and we wish to preserve the alignment of each line.
882                //
883                // ```TypeScript
884                // ()
885                //     : any =>
886                //     1;
887                // ```
888                //
889                // ```TypeScript
890                // (
891                //         ) =>
892                //     1;
893                // ```
894
895                let mut pos = ret.span.hi - BytePos(1);
896                while !self.src.as_bytes()[pos.0 as usize - 1].is_utf8_char_boundary() {
897                    self.add_overwrite(pos, b' ');
898                    pos = pos - BytePos(1);
899                }
900
901                self.add_overwrite(pos, b')');
902            }
903        }
904
905        n.params.visit_with(self);
906        n.body.visit_with(self);
907    }
908
909    fn visit_return_stmt(&mut self, n: &ReturnStmt) {
910        let Some(arg) = n.arg.as_deref() else {
911            return;
912        };
913
914        arg.visit_with(self);
915
916        let Some(arrow_expr) = arg.as_arrow() else {
917            return;
918        };
919
920        if arrow_expr.is_async {
921            // We have already handled type parameters in `visit_arrow_expr`.
922            return;
923        }
924
925        self.fix_asi_in_arrow_expr(arrow_expr);
926    }
927
928    fn visit_yield_expr(&mut self, n: &YieldExpr) {
929        let Some(arg) = &n.arg else {
930            return;
931        };
932
933        arg.visit_with(self);
934
935        let Some(arrow_expr) = arg.as_arrow() else {
936            return;
937        };
938
939        if arrow_expr.is_async {
940            // We have already handled type parameters in `visit_arrow_expr`.
941            return;
942        }
943
944        self.fix_asi_in_arrow_expr(arrow_expr);
945    }
946
947    fn visit_throw_stmt(&mut self, n: &ThrowStmt) {
948        let arg = &n.arg;
949
950        arg.visit_with(self);
951
952        let Some(arrow_expr) = arg.as_arrow() else {
953            return;
954        };
955
956        if arrow_expr.is_async {
957            // We have already handled type parameters in `visit_arrow_expr`.
958            return;
959        }
960
961        self.fix_asi_in_arrow_expr(arrow_expr);
962    }
963
964    fn visit_binding_ident(&mut self, n: &BindingIdent) {
965        n.visit_children_with(self);
966
967        if n.optional {
968            let mark_index = if let Some(type_ann) = &n.type_ann {
969                self.get_prev_token_index(type_ann.span.lo - BytePos(1))
970            } else {
971                self.get_next_token_index(n.span.hi)
972            };
973
974            self.strip_optional_mark(mark_index);
975        }
976    }
977
978    fn visit_class(&mut self, n: &Class) {
979        if n.is_abstract {
980            let mark_pos = n.decorators.last().map_or(n.span.lo, |d| d.span.hi);
981            let r#abstract = self.get_next_token_index(mark_pos);
982
983            self.strip_token(
984                r#abstract,
985                Token::Word(Word::Ident(IdentLike::Known(KnownIdent::Abstract))),
986            )
987        }
988
989        if !n.implements.is_empty() {
990            let implements =
991                self.get_prev_token(n.implements.first().unwrap().span.lo - BytePos(1));
992            debug_assert_eq!(
993                implements.token,
994                Token::Word(Word::Ident(IdentLike::Known(KnownIdent::Implements)))
995            );
996
997            let last = n.implements.last().unwrap();
998            let span = span(implements.span.lo, last.span.hi);
999            self.add_replacement(span);
1000        }
1001
1002        n.visit_children_with(self);
1003    }
1004
1005    fn visit_constructor(&mut self, n: &Constructor) {
1006        if n.body.is_none() {
1007            self.add_replacement(n.span);
1008            return;
1009        }
1010
1011        // TODO(AST): constructor can not be optional
1012        debug_assert!(!n.is_optional);
1013
1014        if n.accessibility.is_some() {
1015            self.strip_class_modifier(n.span.lo, n.key.span_lo());
1016        }
1017
1018        n.visit_children_with(self);
1019    }
1020
1021    fn visit_class_method(&mut self, n: &ClassMethod) {
1022        if n.function.body.is_none() || n.is_abstract {
1023            self.add_replacement(n.span);
1024            return;
1025        }
1026
1027        let has_modifier = n.is_override || n.accessibility.is_some();
1028
1029        // @foo public m(): void {}
1030        let start_pos = n
1031            .function
1032            .decorators
1033            .last()
1034            .map_or(n.span.lo, |d| d.span.hi);
1035
1036        if has_modifier {
1037            self.strip_class_modifier(start_pos, n.key.span_lo());
1038        }
1039
1040        if n.is_optional {
1041            let mark_index = self.get_next_token_index(n.key.span_hi());
1042            self.strip_optional_mark(mark_index);
1043        }
1044
1045        // It's dangerous to strip TypeScript modifiers if the key is computed, a
1046        // generator, or `in`/`instanceof` keyword. However, it is safe to do so
1047        // if the key is preceded by a `static` keyword or decorators.
1048        //
1049        // `public [foo]()`
1050        // `;      [foo]()`
1051        //
1052        // `public *foo()`
1053        // `;      *foo()`
1054        //
1055        // `public in()`
1056        // `;      in()`
1057        if has_modifier
1058            && !n.is_static
1059            && n.function.decorators.is_empty()
1060            && (n.key.is_computed()
1061                || n.function.is_generator
1062                || n.key
1063                    .as_ident()
1064                    .filter(|k| matches!(k.sym.as_ref(), "in" | "instanceof"))
1065                    .is_some())
1066        {
1067            self.add_overwrite(start_pos, b';');
1068        }
1069
1070        n.visit_children_with(self);
1071    }
1072
1073    fn visit_class_prop(&mut self, n: &ClassProp) {
1074        if n.declare || n.is_abstract {
1075            self.add_replacement(n.span);
1076            return;
1077        }
1078
1079        let has_modifier = n.readonly || n.is_override || n.accessibility.is_some();
1080        let start_pos = n.decorators.last().map_or(n.span.lo, |d| d.span.hi);
1081
1082        if has_modifier {
1083            self.strip_class_modifier(start_pos, n.key.span_lo());
1084        }
1085
1086        if n.is_optional {
1087            let mark_index = self.get_next_token_index(n.key.span_hi());
1088            self.strip_optional_mark(mark_index);
1089        }
1090        if n.definite {
1091            let mark_index = self.get_next_token_index(n.key.span_hi());
1092            self.strip_definite_mark(mark_index);
1093        }
1094
1095        // It's dangerous to strip types if the key is `get`, `set`, or `static`.
1096        if n.value.is_none() {
1097            if let Some(key) = n.key.as_ident() {
1098                if matches!(key.sym.as_ref(), "get" | "set" | "static") {
1099                    // `get: number`
1100                    // `get;       `
1101                    if let Some(type_ann) = &n.type_ann {
1102                        self.add_overwrite(type_ann.span.lo, b';');
1103                    }
1104                }
1105            }
1106        }
1107
1108        // `private [foo]`
1109        // `;       [foo]`
1110        //
1111        // `private in`
1112        // `;       in`
1113        if !n.is_static
1114            && has_modifier
1115            && n.decorators.is_empty()
1116            && (n.key.is_computed()
1117                || n.key
1118                    .as_ident()
1119                    .filter(|k| matches!(k.sym.as_ref(), "in" | "instanceof"))
1120                    .is_some())
1121        {
1122            self.add_overwrite(start_pos, b';');
1123        }
1124
1125        n.visit_children_with(self);
1126    }
1127
1128    fn visit_private_method(&mut self, n: &PrivateMethod) {
1129        debug_assert!(!n.is_override);
1130        debug_assert!(!n.is_abstract);
1131
1132        // Is `private #foo()` valid?
1133        if n.accessibility.is_some() {
1134            let start_pos = n
1135                .function
1136                .decorators
1137                .last()
1138                .map_or(n.span.lo, |d| d.span.hi);
1139
1140            self.strip_class_modifier(start_pos, n.key.span.lo);
1141        }
1142
1143        if n.is_optional {
1144            let mark_index = self.get_next_token_index(n.key.span.hi);
1145            self.strip_optional_mark(mark_index);
1146        }
1147
1148        n.visit_children_with(self);
1149    }
1150
1151    fn visit_private_prop(&mut self, n: &PrivateProp) {
1152        debug_assert!(!n.is_override);
1153
1154        if n.readonly || n.accessibility.is_some() {
1155            let start_pos = n.decorators.last().map_or(n.span.lo, |d| d.span.hi);
1156            self.strip_class_modifier(start_pos, n.key.span.lo);
1157        }
1158
1159        if n.is_optional {
1160            let mark_index = self.get_next_token_index(n.key.span.hi);
1161            self.strip_optional_mark(mark_index);
1162        }
1163
1164        if n.definite {
1165            let mark_index = self.get_next_token_index(n.key.span.hi);
1166            self.strip_definite_mark(mark_index);
1167        }
1168
1169        n.visit_children_with(self);
1170    }
1171
1172    fn visit_auto_accessor(&mut self, n: &AutoAccessor) {
1173        if n.is_abstract {
1174            self.add_replacement(n.span);
1175            return;
1176        }
1177
1178        let start_pos = n.decorators.last().map_or(n.span.lo, |d| d.span.hi);
1179
1180        self.strip_class_modifier(start_pos, n.key.span_lo());
1181
1182        if n.definite {
1183            let mark_index = self.get_next_token_index(n.key.span_hi());
1184            self.strip_definite_mark(mark_index);
1185        }
1186
1187        n.visit_children_with(self);
1188    }
1189
1190    fn visit_array_pat(&mut self, n: &ArrayPat) {
1191        if n.optional {
1192            let mark_index = if let Some(type_ann) = &n.type_ann {
1193                self.get_prev_token_index(type_ann.span.lo - BytePos(1))
1194            } else {
1195                self.get_next_token_index(n.span.hi)
1196            };
1197            self.strip_optional_mark(mark_index);
1198        }
1199
1200        n.visit_children_with(self);
1201    }
1202
1203    fn visit_object_pat(&mut self, n: &ObjectPat) {
1204        if n.optional {
1205            let mark_index = if let Some(type_ann) = &n.type_ann {
1206                self.get_prev_token_index(type_ann.span.lo - BytePos(1))
1207            } else {
1208                self.get_next_token_index(n.span.hi)
1209            };
1210            self.strip_optional_mark(mark_index);
1211        }
1212
1213        n.visit_children_with(self);
1214    }
1215
1216    fn visit_export_all(&mut self, n: &ExportAll) {
1217        if n.type_only {
1218            self.add_replacement(n.span);
1219            self.fix_asi(n.span);
1220            return;
1221        }
1222
1223        n.visit_children_with(self);
1224    }
1225
1226    fn visit_export_decl(&mut self, n: &ExportDecl) {
1227        if n.decl.is_ts_declare() || n.decl.is_uninstantiated() {
1228            self.add_replacement(n.span);
1229            self.fix_asi(n.span);
1230            return;
1231        }
1232
1233        n.visit_children_with(self);
1234    }
1235
1236    fn visit_export_default_decl(&mut self, n: &ExportDefaultDecl) {
1237        if n.decl.is_ts_declare() || n.decl.is_uninstantiated() {
1238            self.add_replacement(n.span);
1239            self.fix_asi(n.span);
1240            return;
1241        }
1242
1243        n.visit_children_with(self);
1244    }
1245
1246    fn visit_decl(&mut self, n: &Decl) {
1247        if n.is_ts_declare() || n.is_uninstantiated() {
1248            self.add_replacement(n.span());
1249            self.fix_asi(n.span());
1250            return;
1251        }
1252
1253        n.visit_children_with(self);
1254    }
1255
1256    fn visit_import_decl(&mut self, n: &ImportDecl) {
1257        if n.type_only {
1258            self.add_replacement(n.span);
1259            self.fix_asi(n.span);
1260            return;
1261        }
1262
1263        n.visit_children_with(self);
1264    }
1265
1266    fn visit_import_specifiers(&mut self, n: &[ImportSpecifier]) {
1267        for import in n {
1268            if let ImportSpecifier::Named(import) = import {
1269                if import.is_type_only {
1270                    let mut span = import.span;
1271                    let comma = self.get_next_token(import.span.hi);
1272                    if comma.token == Token::Comma {
1273                        span = span.with_hi(comma.span.hi);
1274                    } else {
1275                        debug_assert_eq!(comma.token, Token::RBrace);
1276                    }
1277                    self.add_replacement(span);
1278                }
1279            }
1280        }
1281    }
1282
1283    fn visit_named_export(&mut self, n: &NamedExport) {
1284        if n.type_only {
1285            self.add_replacement(n.span);
1286            self.fix_asi(n.span);
1287            return;
1288        }
1289
1290        for export in n.specifiers.iter() {
1291            if let ExportSpecifier::Named(e) = export {
1292                if e.is_type_only {
1293                    let mut span = e.span;
1294                    let comma = self.get_next_token(e.span.hi);
1295                    if comma.token == Token::Comma {
1296                        span = span.with_hi(comma.span.hi);
1297                    } else {
1298                        debug_assert_eq!(comma.token, Token::RBrace);
1299                    }
1300                    self.add_replacement(span);
1301                }
1302            }
1303        }
1304    }
1305
1306    fn visit_params(&mut self, n: &[Param]) {
1307        if let Some(p) = n.first().filter(|param| {
1308            matches!(
1309                &param.pat,
1310                Pat::Ident(id) if id.sym == "this"
1311            )
1312        }) {
1313            let mut span = p.span;
1314
1315            let comma = self.get_next_token(span.hi);
1316            if comma.token == Token::Comma {
1317                span = span.with_hi(comma.span.hi);
1318            } else {
1319                debug_assert_eq!(comma.token, Token::RParen);
1320            }
1321            self.add_replacement(span);
1322
1323            n[1..].visit_children_with(self);
1324
1325            return;
1326        }
1327
1328        n.visit_children_with(self);
1329    }
1330
1331    fn visit_ts_as_expr(&mut self, n: &TsAsExpr) {
1332        self.add_replacement(span(n.expr.span().hi, n.span.hi));
1333        let TokenAndSpan {
1334            token,
1335            span: as_span,
1336            ..
1337        } = self.get_next_token(n.expr.span_hi());
1338        debug_assert_eq!(
1339            token,
1340            &Token::Word(Word::Ident(IdentLike::Known(KnownIdent::As)))
1341        );
1342        self.fix_asi_in_expr(span(as_span.lo, n.span.hi));
1343
1344        n.expr.visit_children_with(self);
1345    }
1346
1347    fn visit_ts_const_assertion(&mut self, n: &TsConstAssertion) {
1348        self.add_replacement(span(n.expr.span().hi, n.span.hi));
1349
1350        n.expr.visit_children_with(self);
1351    }
1352
1353    fn visit_ts_export_assignment(&mut self, n: &TsExportAssignment) {
1354        HANDLER.with(|handler| {
1355            handler
1356                .struct_span_err(
1357                    n.span,
1358                    "TypeScript export assignment is not supported in strip-only mode",
1359                )
1360                .code(DiagnosticId::Error("UnsupportedSyntax".into()))
1361                .emit();
1362        });
1363    }
1364
1365    fn visit_ts_import_equals_decl(&mut self, n: &TsImportEqualsDecl) {
1366        if n.is_type_only {
1367            self.add_replacement(n.span);
1368            self.fix_asi(n.span);
1369            return;
1370        }
1371
1372        HANDLER.with(|handler| {
1373            handler
1374                .struct_span_err(
1375                    n.span,
1376                    "TypeScript import equals declaration is not supported in strip-only mode",
1377                )
1378                .code(DiagnosticId::Error("UnsupportedSyntax".into()))
1379                .emit();
1380        });
1381    }
1382
1383    fn visit_ts_index_signature(&mut self, n: &TsIndexSignature) {
1384        self.add_replacement(n.span);
1385    }
1386
1387    fn visit_ts_instantiation(&mut self, n: &TsInstantiation) {
1388        self.add_replacement(span(n.expr.span().hi, n.span.hi));
1389
1390        n.expr.visit_children_with(self);
1391    }
1392
1393    fn visit_ts_enum_decl(&mut self, e: &TsEnumDecl) {
1394        HANDLER.with(|handler| {
1395            handler
1396                .struct_span_err(
1397                    e.span,
1398                    "TypeScript enum is not supported in strip-only mode",
1399                )
1400                .code(DiagnosticId::Error("UnsupportedSyntax".into()))
1401                .emit();
1402        });
1403    }
1404
1405    fn visit_ts_module_decl(&mut self, n: &TsModuleDecl) {
1406        HANDLER.with(|handler| {
1407            handler
1408                .struct_span_err(
1409                    n.span(),
1410                    "TypeScript namespace declaration is not supported in strip-only mode",
1411                )
1412                .code(DiagnosticId::Error("UnsupportedSyntax".into()))
1413                .emit();
1414        });
1415    }
1416
1417    fn visit_ts_non_null_expr(&mut self, n: &TsNonNullExpr) {
1418        self.add_replacement(span(n.span.hi - BytePos(1), n.span.hi));
1419
1420        n.expr.visit_children_with(self);
1421    }
1422
1423    fn visit_ts_param_prop_param(&mut self, n: &TsParamPropParam) {
1424        HANDLER.with(|handler| {
1425            handler
1426                .struct_span_err(
1427                    n.span(),
1428                    "TypeScript parameter property is not supported in strip-only mode",
1429                )
1430                .code(DiagnosticId::Error("UnsupportedSyntax".into()))
1431                .emit();
1432        });
1433    }
1434
1435    fn visit_ts_satisfies_expr(&mut self, n: &TsSatisfiesExpr) {
1436        self.add_replacement(span(n.expr.span().hi, n.span.hi));
1437
1438        let TokenAndSpan {
1439            token,
1440            span: as_span,
1441            ..
1442        } = self.get_next_token(n.expr.span_hi());
1443        debug_assert_eq!(
1444            token,
1445            &Token::Word(Word::Ident(IdentLike::Known(KnownIdent::Satisfies)))
1446        );
1447        self.fix_asi_in_expr(span(as_span.lo, n.span.hi));
1448
1449        n.expr.visit_children_with(self);
1450    }
1451
1452    fn visit_ts_type_alias_decl(&mut self, n: &TsTypeAliasDecl) {
1453        self.add_replacement(n.span);
1454        self.fix_asi(n.span);
1455    }
1456
1457    fn visit_ts_type_ann(&mut self, n: &TsTypeAnn) {
1458        self.add_replacement(n.span);
1459    }
1460
1461    /// We do not strip type assertion because it's not safe.
1462    ///
1463    /// See https://github.com/swc-project/swc/issues/9295
1464    fn visit_ts_type_assertion(&mut self, n: &TsTypeAssertion) {
1465        HANDLER.with(|handler| {
1466            handler
1467                .struct_span_err(
1468                    n.span,
1469                    "The angle-bracket syntax for type assertions, `<T>expr`, is not supported in \
1470                     type strip mode. Instead, use the 'as' syntax: `expr as T`.",
1471                )
1472                .code(DiagnosticId::Error("UnsupportedSyntax".into()))
1473                .emit();
1474        });
1475
1476        n.expr.visit_children_with(self);
1477    }
1478
1479    fn visit_ts_type_param_decl(&mut self, n: &TsTypeParamDecl) {
1480        self.add_replacement(n.span);
1481    }
1482
1483    fn visit_ts_type_param_instantiation(&mut self, n: &TsTypeParamInstantiation) {
1484        self.add_replacement(span(n.span.lo, n.span.hi));
1485    }
1486
1487    fn visit_if_stmt(&mut self, n: &IfStmt) {
1488        n.visit_children_with(self);
1489
1490        if n.cons.is_ts_declare() {
1491            self.add_overwrite(n.cons.span_lo(), b';');
1492        }
1493
1494        if let Some(alt) = &n.alt {
1495            if alt.is_ts_declare() {
1496                self.add_overwrite(alt.span_lo(), b';');
1497            }
1498        }
1499    }
1500
1501    fn visit_for_stmt(&mut self, n: &ForStmt) {
1502        n.visit_children_with(self);
1503
1504        if n.body.is_ts_declare() {
1505            self.add_overwrite(n.body.span_lo(), b';');
1506        }
1507    }
1508
1509    fn visit_for_in_stmt(&mut self, n: &ForInStmt) {
1510        n.visit_children_with(self);
1511
1512        if n.body.is_ts_declare() {
1513            self.add_overwrite(n.body.span_lo(), b';');
1514        }
1515    }
1516
1517    fn visit_for_of_stmt(&mut self, n: &ForOfStmt) {
1518        n.visit_children_with(self);
1519
1520        if n.body.is_ts_declare() {
1521            self.add_overwrite(n.body.span_lo(), b';');
1522        }
1523    }
1524
1525    fn visit_while_stmt(&mut self, n: &WhileStmt) {
1526        n.visit_children_with(self);
1527
1528        if n.body.is_ts_declare() {
1529            self.add_overwrite(n.body.span_lo(), b';');
1530        }
1531    }
1532
1533    fn visit_do_while_stmt(&mut self, n: &DoWhileStmt) {
1534        n.visit_children_with(self);
1535
1536        if n.body.is_ts_declare() {
1537            self.add_overwrite(n.body.span_lo(), b';');
1538        }
1539    }
1540
1541    fn visit_getter_prop(&mut self, n: &GetterProp) {
1542        let l_parern_index = self.get_next_token_index(n.key.span_hi());
1543        let l_parern = &self.tokens[l_parern_index];
1544        debug_assert_eq!(l_parern.token, Token::LParen);
1545
1546        let r_parern_pos = n.type_ann.as_ref().map_or(n.body.span_lo(), |t| t.span.lo) - BytePos(1);
1547        let r_parern = self.get_prev_token(r_parern_pos);
1548        debug_assert_eq!(r_parern.token, Token::RParen);
1549
1550        let span = span(l_parern.span.lo + BytePos(1), r_parern.span.hi - BytePos(1));
1551        self.add_replacement(span);
1552
1553        n.visit_children_with(self);
1554    }
1555
1556    fn visit_setter_prop(&mut self, n: &SetterProp) {
1557        if let Some(this_param) = &n.this_param {
1558            self.add_replacement(this_param.span());
1559
1560            let comma = self.get_prev_token(n.param.span_lo() - BytePos(1));
1561            debug_assert_eq!(comma.token, Token::Comma);
1562
1563            self.add_replacement(comma.span);
1564        }
1565
1566        n.visit_children_with(self);
1567    }
1568}
1569
1570#[inline(always)]
1571fn is_new_line(c: char) -> bool {
1572    matches!(c, '\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}')
1573}
1574trait IsTsDecl {
1575    fn is_ts_declare(&self) -> bool;
1576}
1577
1578impl IsTsDecl for Decl {
1579    fn is_ts_declare(&self) -> bool {
1580        match self {
1581            Self::TsInterface(..) | Self::TsTypeAlias(..) => true,
1582
1583            Self::TsModule(module) => module.declare || matches!(module.id, TsModuleName::Str(..)),
1584            Self::TsEnum(ref r#enum) => r#enum.declare,
1585
1586            Self::Var(ref var) => var.declare,
1587            Self::Fn(FnDecl { declare: true, .. })
1588            | Self::Class(ClassDecl { declare: true, .. }) => true,
1589
1590            Self::Fn(FnDecl { function, .. }) => function.body.is_none(),
1591
1592            _ => false,
1593        }
1594    }
1595}
1596
1597impl IsTsDecl for Stmt {
1598    fn is_ts_declare(&self) -> bool {
1599        self.as_decl().is_some_and(IsTsDecl::is_ts_declare)
1600    }
1601}
1602
1603impl IsTsDecl for DefaultDecl {
1604    fn is_ts_declare(&self) -> bool {
1605        match self {
1606            Self::Class(..) => false,
1607            Self::Fn(r#fn) => r#fn.function.body.is_none(),
1608            Self::TsInterfaceDecl(..) => true,
1609        }
1610    }
1611}
1612
1613trait IsUninstantiated {
1614    fn is_uninstantiated(&self) -> bool;
1615}
1616
1617impl IsUninstantiated for TsNamespaceBody {
1618    fn is_uninstantiated(&self) -> bool {
1619        match self {
1620            Self::TsModuleBlock(block) => {
1621                block.body.iter().all(IsUninstantiated::is_uninstantiated)
1622            }
1623            Self::TsNamespaceDecl(decl) => decl.body.is_uninstantiated(),
1624        }
1625    }
1626}
1627
1628impl IsUninstantiated for ModuleItem {
1629    fn is_uninstantiated(&self) -> bool {
1630        match self {
1631            Self::Stmt(stmt) => stmt.is_uninstantiated(),
1632            Self::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { decl, .. })) => {
1633                decl.is_uninstantiated()
1634            }
1635            _ => false,
1636        }
1637    }
1638}
1639
1640impl IsUninstantiated for Stmt {
1641    fn is_uninstantiated(&self) -> bool {
1642        matches!(self, Self::Decl(decl) if decl.is_uninstantiated())
1643    }
1644}
1645
1646impl IsUninstantiated for TsModuleDecl {
1647    fn is_uninstantiated(&self) -> bool {
1648        matches!(&self.body, Some(body) if body.is_uninstantiated())
1649    }
1650}
1651
1652impl IsUninstantiated for Decl {
1653    fn is_uninstantiated(&self) -> bool {
1654        match self {
1655            Self::TsInterface(..) | Self::TsTypeAlias(..) => true,
1656            Self::TsModule(module) => module.is_uninstantiated(),
1657            _ => false,
1658        }
1659    }
1660}
1661
1662impl IsUninstantiated for DefaultDecl {
1663    fn is_uninstantiated(&self) -> bool {
1664        matches!(self, Self::TsInterfaceDecl(..))
1665    }
1666}
1667
1668trait U8Helper {
1669    fn is_utf8_char_boundary(&self) -> bool;
1670}
1671
1672impl U8Helper for u8 {
1673    // Copy from std::core::num::u8
1674    #[inline]
1675    fn is_utf8_char_boundary(&self) -> bool {
1676        // This is bit magic equivalent to: b < 128 || b >= 192
1677        (*self as i8) >= -0x40
1678    }
1679}
1680
1681fn span(lo: BytePos, hi: BytePos) -> Span {
1682    Span::new(lo, hi)
1683}