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 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 '\u{0009}' | '\u{0000B}' | '\u{000C}' | '\u{FEFF}' => continue,
353 '\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 '\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}' => continue,
360 _ => match c.len_utf8() {
361 1 => {
362 code[i] = 0x20;
364 }
365 2 => {
366 code[i] = 0xc2;
368 code[i + 1] = 0xa0;
369 }
370 3 => {
371 code[i] = 0xe2;
373 code[i + 1] = 0x80;
374 code[i + 2] = 0x82;
375 }
376 4 => {
377 code[i] = 0x20;
381 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 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
531impl 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 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 replacements: Vec<(BytePos, BytePos)>,
602
603 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 if let Some(type_ann) = &n.type_ann {
1102 self.add_overwrite(type_ann.span.lo, b';');
1103 }
1104 }
1105 }
1106 }
1107
1108 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 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 ¶m.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 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 #[inline]
1675 fn is_utf8_char_boundary(&self) -> bool {
1676 (*self as i8) >= -0x40
1678 }
1679}
1680
1681fn span(lo: BytePos, hi: BytePos) -> Span {
1682 Span::new(lo, hi)
1683}