1use std::fmt::Display;
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_parser::{
25 lexer::Lexer,
26 unstable::{Capturing, Token, TokenAndSpan},
27 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 mut parser = Parser::new_from(lexer);
255
256 let program = match options.module {
257 Some(true) => parser.parse_module().map(Program::Module),
258 Some(false) => parser.parse_script().map(Program::Script),
259 None => parser.parse_program(),
260 };
261 let errors = parser.take_errors();
262 let mut tokens = parser.input_mut().iter_mut().take();
263
264 let mut program = match program {
265 Ok(program) => program,
266 Err(err) => {
267 err.into_diagnostic(handler)
268 .code(DiagnosticId::Error("InvalidSyntax".into()))
269 .emit();
270
271 for e in errors {
272 e.into_diagnostic(handler)
273 .code(DiagnosticId::Error("InvalidSyntax".into()))
274 .emit();
275 }
276
277 return Err(TsError {
278 message: "Syntax error".to_string(),
279 code: ErrorCode::InvalidSyntax,
280 });
281 }
282 };
283
284 if !errors.is_empty() {
285 for e in errors {
286 e.into_diagnostic(handler)
287 .code(DiagnosticId::Error("InvalidSyntax".into()))
288 .emit();
289 }
290
291 return Err(TsError {
292 message: "Syntax error".to_string(),
293 code: ErrorCode::InvalidSyntax,
294 });
295 }
296
297 drop(parser);
298
299 let deprecated_ts_module_as_error = options.deprecated_ts_module_as_error.unwrap_or_default();
300
301 match options.mode {
302 Mode::StripOnly => {
303 tokens.sort_by_key(|t| t.span);
304
305 if deprecated_ts_module_as_error {
306 program.visit_with(&mut ErrorOnTsModule {
307 src: &fm.src,
308 tokens: &tokens,
309 });
310 if handler.has_errors() {
311 return Err(TsError {
312 message: "Unsupported syntax".to_string(),
313 code: ErrorCode::UnsupportedSyntax,
314 });
315 }
316 }
317
318 let mut ts_strip = TsStrip::new(fm.src.clone(), tokens);
320
321 program.visit_with(&mut ts_strip);
322 if handler.has_errors() {
323 return Err(TsError {
324 message: "Unsupported syntax".to_string(),
325 code: ErrorCode::UnsupportedSyntax,
326 });
327 }
328
329 let replacements = ts_strip.replacements;
330 let overwrites = ts_strip.overwrites;
331
332 if replacements.is_empty() && overwrites.is_empty() {
333 return Ok(TransformOutput {
334 code: fm.src.to_string(),
335 map: Default::default(),
336 });
337 }
338
339 let source = fm.src.clone();
340 let mut code = fm.src.to_string().into_bytes();
341
342 for r in replacements {
343 let (start, end) = (r.0 .0 as usize - 1, r.1 .0 as usize - 1);
344
345 for (i, c) in source[start..end].char_indices() {
346 let i = start + i;
347 match c {
348 '\u{0009}' | '\u{0000B}' | '\u{000C}' | '\u{FEFF}' => continue,
350 '\u{0020}' | '\u{00A0}' | '\u{1680}' | '\u{2000}' | '\u{2001}'
352 | '\u{2002}' | '\u{2003}' | '\u{2004}' | '\u{2005}' | '\u{2006}'
353 | '\u{2007}' | '\u{2008}' | '\u{2009}' | '\u{200A}' | '\u{202F}'
354 | '\u{205F}' | '\u{3000}' => continue,
355 '\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}' => continue,
357 _ => match c.len_utf8() {
358 1 => {
359 code[i] = 0x20;
361 }
362 2 => {
363 code[i] = 0xc2;
365 code[i + 1] = 0xa0;
366 }
367 3 => {
368 code[i] = 0xe2;
370 code[i + 1] = 0x80;
371 code[i + 2] = 0x82;
372 }
373 4 => {
374 code[i] = 0x20;
378 code[i + 1] = 0xef;
380 code[i + 2] = 0xbb;
381 code[i + 3] = 0xbf;
382 }
383 _ => unreachable!(),
384 },
385 }
386 }
387 }
388
389 for (i, v) in overwrites {
390 code[i.0 as usize - 1] = v;
391 }
392
393 let code = if cfg!(debug_assertions) {
394 String::from_utf8(code).map_err(|err| TsError {
395 message: format!("failed to convert to utf-8: {err}"),
396 code: ErrorCode::Unknown,
397 })?
398 } else {
399 unsafe { String::from_utf8_unchecked(code) }
402 };
403
404 Ok(TransformOutput {
405 code,
406 map: Default::default(),
407 })
408 }
409
410 Mode::Transform => {
411 let unresolved_mark = Mark::new();
412 let top_level_mark = Mark::new();
413
414 HELPERS.set(&Helpers::new(false), || {
415 program.mutate(&mut resolver(unresolved_mark, top_level_mark, true));
416
417 if deprecated_ts_module_as_error {
418 tokens.sort_by_key(|t| t.span);
419
420 program.visit_with(&mut ErrorOnTsModule {
421 src: &fm.src,
422 tokens: &tokens,
423 });
424 if handler.has_errors() {
425 return Err(TsError {
426 message: "Unsupported syntax".to_string(),
427 code: ErrorCode::UnsupportedSyntax,
428 });
429 }
430 }
431
432 let transform = options.transform.unwrap_or_default();
433
434 program.mutate(&mut typescript::typescript(
435 transform.typescript,
436 unresolved_mark,
437 top_level_mark,
438 ));
439
440 #[cfg(feature = "nightly")]
441 program.mutate(&mut swc_ecma_transforms_react::jsx(
442 cm.clone(),
443 Some(comments.clone()),
444 swc_ecma_transforms_react::Options {
445 next: Some(true),
446 runtime: Some(swc_ecma_transforms_react::Runtime::Automatic),
447 import_source: transform
448 .jsx
449 .as_ref()
450 .and_then(|jsx| jsx.import_source.clone()),
451 development: match transform.jsx {
452 Some(JsxConfig {
453 transform: Some(transform),
454 ..
455 }) => Some(matches!(transform, JsxTransform::ReactJsxDev)),
456 _ => None,
457 },
458 refresh: None,
459 ..Default::default()
460 },
461 top_level_mark,
462 unresolved_mark,
463 ));
464
465 program.mutate(&mut inject_helpers(unresolved_mark));
466
467 program.mutate(&mut hygiene());
468
469 program.mutate(&mut fixer(Some(&comments)));
470
471 Ok(())
472 })?;
473
474 let mut src = std::vec::Vec::with_capacity(source_len);
475 let mut src_map_buf = if options.source_map {
476 Some(Vec::new())
477 } else {
478 None
479 };
480
481 {
482 let mut emitter = swc_ecma_codegen::Emitter {
483 cfg: swc_ecma_codegen::Config::default(),
484 comments: if options.source_map {
485 Some(&comments)
486 } else {
487 None
488 },
489 cm: cm.clone(),
490 wr: swc_ecma_codegen::text_writer::JsWriter::new(
491 cm.clone(),
492 "\n",
493 &mut src,
494 src_map_buf.as_mut(),
495 ),
496 };
497
498 emitter.emit_program(&program).unwrap();
499
500 let map = src_map_buf
501 .map(|map| {
502 let map = cm.build_source_map(&map, None, DefaultSourceMapGenConfig);
503
504 let mut s = std::vec::Vec::new();
505 map.to_writer(&mut s)
506 .context("failed to write source map")?;
507
508 String::from_utf8(s).context("source map was not utf8")
509 })
510 .transpose()?;
511
512 Ok(TransformOutput {
513 code: String::from_utf8(src).context("generated code was not utf-8")?,
514 map,
515 })
516 }
517 }
518 }
519}
520
521struct ErrorOnTsModule<'a> {
522 src: &'a str,
523 tokens: &'a [TokenAndSpan],
524}
525
526impl Visit for ErrorOnTsModule<'_> {
529 fn visit_stmt(&mut self, n: &Stmt) {
530 if n.is_decl() {
531 n.visit_children_with(self);
532 }
533 }
534
535 fn visit_decl(&mut self, n: &Decl) {
536 if n.is_ts_module() {
537 n.visit_children_with(self);
538 }
539 }
540
541 fn visit_module_decl(&mut self, n: &ModuleDecl) {
542 if n.is_export_decl() {
543 n.visit_children_with(self);
544 }
545 }
546
547 fn visit_ts_module_decl(&mut self, n: &TsModuleDecl) {
548 n.visit_children_with(self);
549
550 if n.global || n.id.is_str() {
551 return;
552 }
553
554 let mut pos = n.span.lo;
555
556 if n.declare {
557 let declare_index = self
558 .tokens
559 .binary_search_by_key(&pos, |t| t.span.lo)
560 .unwrap();
561
562 debug_assert_eq!(self.tokens[declare_index].token, Token::Declare);
563
564 let TokenAndSpan { token, span, .. } = &self.tokens[declare_index + 1];
565 if matches!(token, Token::Namespace) {
569 return;
570 }
571
572 pos = span.lo;
573 } else if self.src.as_bytes()[pos.0 as usize - 1] != b'm' {
574 return;
575 }
576
577 if HANDLER.is_set() {
578 HANDLER.with(|handler| {
579 handler
580 .struct_span_err(
581 span(pos, n.id.span().hi),
582 "`module` keyword is not supported. Use `namespace` instead.",
583 )
584 .code(DiagnosticId::Error("UnsupportedSyntax".into()))
585 .emit();
586 });
587 }
588 }
589}
590
591struct TsStrip {
592 src: BytesStr,
593
594 replacements: Vec<(BytePos, BytePos)>,
596
597 overwrites: Vec<(BytePos, u8)>,
599
600 tokens: std::vec::Vec<TokenAndSpan>,
601}
602
603impl TsStrip {
604 fn new(src: BytesStr, tokens: std::vec::Vec<TokenAndSpan>) -> Self {
605 TsStrip {
606 src,
607 replacements: Default::default(),
608 overwrites: Default::default(),
609 tokens,
610 }
611 }
612}
613
614impl TsStrip {
615 fn add_replacement(&mut self, span: Span) {
616 self.replacements.push((span.lo, span.hi));
617 }
618
619 fn add_overwrite(&mut self, pos: BytePos, value: u8) {
620 self.overwrites.push((pos, value));
621 }
622
623 fn get_src_slice(&self, span: Span) -> &str {
624 &self.src[(span.lo.0 - 1) as usize..(span.hi.0 - 1) as usize]
625 }
626
627 fn get_next_token_index(&self, pos: BytePos) -> usize {
628 let index = self.tokens.binary_search_by_key(&pos, |t| t.span.lo);
629 match index {
630 Ok(index) => index,
631 Err(index) => index,
632 }
633 }
634
635 fn get_next_token(&self, pos: BytePos) -> &TokenAndSpan {
636 &self.tokens[self.get_next_token_index(pos)]
637 }
638
639 fn get_prev_token_index(&self, pos: BytePos) -> usize {
640 let index = self.tokens.binary_search_by_key(&pos, |t| t.span.lo);
641 match index {
642 Ok(index) => index,
643 Err(index) => index - 1,
644 }
645 }
646
647 fn get_prev_token(&self, pos: BytePos) -> &TokenAndSpan {
648 &self.tokens[self.get_prev_token_index(pos)]
649 }
650
651 fn fix_asi(&mut self, span: Span) {
652 let index = self.get_prev_token_index(span.lo);
653 if index == 0 {
654 return;
656 }
657
658 let TokenAndSpan {
659 token: prev_token,
660 span: prev_span,
661 ..
662 } = &self.tokens[index - 1];
663
664 let index = self.get_prev_token_index(span.hi - BytePos(1));
665 if index == self.tokens.len() - 1 {
666 return;
668 }
669
670 let TokenAndSpan {
671 token,
672 had_line_break,
673 ..
674 } = &self.tokens[index + 1];
675
676 if !*had_line_break {
677 return;
678 }
679
680 match token {
683 Token::LParen
684 | Token::LBracket
685 | Token::NoSubstitutionTemplateLiteral
686 | Token::Plus
687 | Token::Minus
688 | Token::Regex => {
689 if prev_token == &Token::Semi {
690 self.add_overwrite(prev_span.lo, b';');
691 return;
692 }
693
694 self.add_overwrite(span.lo, b';');
695 }
696
697 _ => {}
698 }
699 }
700
701 fn fix_asi_in_expr(&mut self, span: Span) {
702 let index = self.get_prev_token_index(span.hi - BytePos(1));
703 if index == self.tokens.len() - 1 {
704 return;
705 }
706
707 if let TokenAndSpan {
708 token: Token::LParen | Token::LBracket | Token::NoSubstitutionTemplateLiteral,
710 had_line_break: true,
711 ..
712 } = &self.tokens[index + 1]
713 {
714 self.add_overwrite(span.lo, b';');
715 }
716 }
717
718 fn strip_class_modifier(&mut self, mut start_pos: BytePos, key_pos: BytePos) {
719 let mut index = self.get_next_token_index(start_pos);
720
721 while start_pos < key_pos {
722 let TokenAndSpan { token, span, .. } = &self.tokens[index];
723 start_pos = span.hi;
724 index += 1;
725
726 let next = &self.tokens[index];
727
728 if next.had_line_break {
729 return;
730 }
731
732 if !next.token.is_word()
735 && !matches!(
736 next.token,
737 Token::LBracket
738 | Token::LBrace
739 | Token::Asterisk
740 | Token::DotDotDot
741 | Token::Hash
742 | Token::Str
743 | Token::Num
744 | Token::BigInt
745 )
746 {
747 return;
748 }
749
750 match token {
751 Token::Static => {
752 continue;
753 }
754 Token::Readonly | Token::Public | Token::Protected | Token::Private => {
755 self.add_replacement(*span);
756 }
757 Token::Override => {
758 self.add_replacement(*span);
759 }
760 _ => {
761 return;
762 }
763 }
764 }
765 }
766
767 fn strip_definite_mark(&mut self, index: usize) {
768 self.strip_token(index, Token::Bang);
769 }
770
771 fn strip_optional_mark(&mut self, index: usize) {
772 self.strip_token(index, Token::QuestionMark);
773 }
774
775 fn strip_token(&mut self, index: usize, expected: Token) {
776 let TokenAndSpan { token, span, .. } = &self.tokens[index];
777 debug_assert_eq!(*token, expected);
778
779 self.add_replacement(*span);
780 }
781
782 fn fix_asi_in_arrow_expr(&mut self, arrow_expr: &ArrowExpr) {
792 if let Some(tp) = &arrow_expr.type_params {
793 let l_paren = self.get_next_token(tp.span.hi);
794 debug_assert_eq!(l_paren.token, Token::LParen);
795
796 let slice = self.get_src_slice(tp.span.with_hi(l_paren.span.lo));
797
798 if !slice.chars().any(is_new_line) {
799 return;
800 }
801
802 let l_paren_pos = l_paren.span.lo;
803 let l_lt_pos = tp.span.lo;
804
805 self.add_overwrite(l_paren_pos, b' ');
806 self.add_overwrite(l_lt_pos, b'(');
807 }
808 }
809}
810
811impl Visit for TsStrip {
812 fn visit_var_declarator(&mut self, n: &VarDeclarator) {
813 if n.definite {
814 if let Some(id) = n.name.as_ident() {
815 let mark_index = self.get_next_token_index(id.span.hi);
816 self.strip_definite_mark(mark_index);
817 };
818 }
819
820 n.visit_children_with(self);
821 }
822
823 fn visit_arrow_expr(&mut self, n: &ArrowExpr) {
824 'type_params: {
825 if let Some(tp) = &n.type_params {
837 self.add_replacement(tp.span);
838
839 if !n.is_async {
840 break 'type_params;
841 }
842
843 let slice = self.get_src_slice(tp.span);
844 if !slice.chars().any(is_new_line) {
845 break 'type_params;
846 }
847
848 let l_paren = self.get_next_token(tp.span.hi);
849 debug_assert_eq!(l_paren.token, Token::LParen);
850 let l_paren_pos = l_paren.span.lo;
851 let l_lt_pos = tp.span.lo;
852
853 self.add_overwrite(l_paren_pos, b' ');
854 self.add_overwrite(l_lt_pos, b'(');
855 }
856 }
857
858 if let Some(ret) = &n.return_type {
859 self.add_replacement(ret.span);
860
861 let r_paren = self.get_prev_token(ret.span.lo - BytePos(1));
862 debug_assert_eq!(r_paren.token, Token::RParen);
863 let arrow = self.get_next_token(ret.span.hi);
864 debug_assert_eq!(arrow.token, Token::Arrow);
865 let span = span(r_paren.span.lo, arrow.span.lo);
866
867 let slice = self.get_src_slice(span);
868 if slice.chars().any(is_new_line) {
869 self.add_replacement(r_paren.span);
870
871 let mut pos = ret.span.hi - BytePos(1);
888 while !self.src.as_bytes()[pos.0 as usize - 1].is_utf8_char_boundary() {
889 self.add_overwrite(pos, b' ');
890 pos = pos - BytePos(1);
891 }
892
893 self.add_overwrite(pos, b')');
894 }
895 }
896
897 n.params.visit_with(self);
898 n.body.visit_with(self);
899 }
900
901 fn visit_return_stmt(&mut self, n: &ReturnStmt) {
902 let Some(arg) = n.arg.as_deref() else {
903 return;
904 };
905
906 arg.visit_with(self);
907
908 let Some(arrow_expr) = arg.as_arrow() else {
909 return;
910 };
911
912 if arrow_expr.is_async {
913 return;
915 }
916
917 self.fix_asi_in_arrow_expr(arrow_expr);
918 }
919
920 fn visit_yield_expr(&mut self, n: &YieldExpr) {
921 let Some(arg) = &n.arg else {
922 return;
923 };
924
925 arg.visit_with(self);
926
927 let Some(arrow_expr) = arg.as_arrow() else {
928 return;
929 };
930
931 if arrow_expr.is_async {
932 return;
934 }
935
936 self.fix_asi_in_arrow_expr(arrow_expr);
937 }
938
939 fn visit_throw_stmt(&mut self, n: &ThrowStmt) {
940 let arg = &n.arg;
941
942 arg.visit_with(self);
943
944 let Some(arrow_expr) = arg.as_arrow() else {
945 return;
946 };
947
948 if arrow_expr.is_async {
949 return;
951 }
952
953 self.fix_asi_in_arrow_expr(arrow_expr);
954 }
955
956 fn visit_binding_ident(&mut self, n: &BindingIdent) {
957 n.visit_children_with(self);
958
959 if n.optional {
960 let mark_index = if let Some(type_ann) = &n.type_ann {
961 self.get_prev_token_index(type_ann.span.lo - BytePos(1))
962 } else {
963 self.get_next_token_index(n.span.hi)
964 };
965
966 self.strip_optional_mark(mark_index);
967 }
968 }
969
970 fn visit_class(&mut self, n: &Class) {
971 if n.is_abstract {
972 let mark_pos = n.decorators.last().map_or(n.span.lo, |d| d.span.hi);
973 let r#abstract = self.get_next_token_index(mark_pos);
974
975 self.strip_token(r#abstract, Token::Abstract)
976 }
977
978 if !n.implements.is_empty() {
979 let implements =
980 self.get_prev_token(n.implements.first().unwrap().span.lo - BytePos(1));
981 debug_assert_eq!(implements.token, Token::Implements);
982
983 let last = n.implements.last().unwrap();
984 let span = span(implements.span.lo, last.span.hi);
985 self.add_replacement(span);
986 }
987
988 n.visit_children_with(self);
989 }
990
991 fn visit_constructor(&mut self, n: &Constructor) {
992 if n.body.is_none() {
993 self.add_replacement(n.span);
994 return;
995 }
996
997 debug_assert!(!n.is_optional);
999
1000 if n.accessibility.is_some() {
1001 self.strip_class_modifier(n.span.lo, n.key.span_lo());
1002 }
1003
1004 n.visit_children_with(self);
1005 }
1006
1007 fn visit_class_method(&mut self, n: &ClassMethod) {
1008 if n.function.body.is_none() || n.is_abstract {
1009 self.add_replacement(n.span);
1010 return;
1011 }
1012
1013 let has_modifier = n.is_override || n.accessibility.is_some();
1014
1015 let start_pos = n
1017 .function
1018 .decorators
1019 .last()
1020 .map_or(n.span.lo, |d| d.span.hi);
1021
1022 if has_modifier {
1023 self.strip_class_modifier(start_pos, n.key.span_lo());
1024 }
1025
1026 if n.is_optional {
1027 let mark_index = self.get_next_token_index(n.key.span_hi());
1028 self.strip_optional_mark(mark_index);
1029 }
1030
1031 if has_modifier
1044 && !n.is_static
1045 && n.function.decorators.is_empty()
1046 && (n.key.is_computed()
1047 || n.function.is_generator
1048 || n.key
1049 .as_ident()
1050 .filter(|k| matches!(k.sym.as_ref(), "in" | "instanceof"))
1051 .is_some())
1052 {
1053 self.add_overwrite(start_pos, b';');
1054 }
1055
1056 n.visit_children_with(self);
1057 }
1058
1059 fn visit_class_prop(&mut self, n: &ClassProp) {
1060 if n.declare || n.is_abstract {
1061 self.add_replacement(n.span);
1062 return;
1063 }
1064
1065 let has_modifier = n.readonly || n.is_override || n.accessibility.is_some();
1066 let start_pos = n.decorators.last().map_or(n.span.lo, |d| d.span.hi);
1067
1068 if has_modifier {
1069 self.strip_class_modifier(start_pos, n.key.span_lo());
1070 }
1071
1072 if n.is_optional {
1073 let mark_index = self.get_next_token_index(n.key.span_hi());
1074 self.strip_optional_mark(mark_index);
1075 }
1076 if n.definite {
1077 let mark_index = self.get_next_token_index(n.key.span_hi());
1078 self.strip_definite_mark(mark_index);
1079 }
1080
1081 if n.value.is_none() {
1083 if let Some(key) = n.key.as_ident() {
1084 if matches!(key.sym.as_ref(), "get" | "set" | "static") {
1085 if let Some(type_ann) = &n.type_ann {
1088 self.add_overwrite(type_ann.span.lo, b';');
1089 }
1090 }
1091 }
1092 }
1093
1094 if !n.is_static
1100 && has_modifier
1101 && n.decorators.is_empty()
1102 && (n.key.is_computed()
1103 || n.key
1104 .as_ident()
1105 .filter(|k| matches!(k.sym.as_ref(), "in" | "instanceof"))
1106 .is_some())
1107 {
1108 self.add_overwrite(start_pos, b';');
1109 }
1110
1111 n.visit_children_with(self);
1112 }
1113
1114 fn visit_private_method(&mut self, n: &PrivateMethod) {
1115 debug_assert!(!n.is_override);
1116 debug_assert!(!n.is_abstract);
1117
1118 if n.function.body.is_none() {
1119 self.add_replacement(n.span);
1120 return;
1121 }
1122
1123 if n.accessibility.is_some() {
1125 let start_pos = n
1126 .function
1127 .decorators
1128 .last()
1129 .map_or(n.span.lo, |d| d.span.hi);
1130
1131 self.strip_class_modifier(start_pos, n.key.span.lo);
1132 }
1133
1134 if n.is_optional {
1135 let mark_index = self.get_next_token_index(n.key.span.hi);
1136 self.strip_optional_mark(mark_index);
1137 }
1138
1139 n.visit_children_with(self);
1140 }
1141
1142 fn visit_private_prop(&mut self, n: &PrivateProp) {
1143 debug_assert!(!n.is_override);
1144
1145 if n.readonly || n.accessibility.is_some() {
1146 let start_pos = n.decorators.last().map_or(n.span.lo, |d| d.span.hi);
1147 self.strip_class_modifier(start_pos, n.key.span.lo);
1148 }
1149
1150 if n.is_optional {
1151 let mark_index = self.get_next_token_index(n.key.span.hi);
1152 self.strip_optional_mark(mark_index);
1153 }
1154
1155 if n.definite {
1156 let mark_index = self.get_next_token_index(n.key.span.hi);
1157 self.strip_definite_mark(mark_index);
1158 }
1159
1160 n.visit_children_with(self);
1161 }
1162
1163 fn visit_auto_accessor(&mut self, n: &AutoAccessor) {
1164 if n.is_abstract {
1165 self.add_replacement(n.span);
1166 return;
1167 }
1168
1169 let start_pos = n.decorators.last().map_or(n.span.lo, |d| d.span.hi);
1170
1171 self.strip_class_modifier(start_pos, n.key.span_lo());
1172
1173 if n.definite {
1174 let mark_index = self.get_next_token_index(n.key.span_hi());
1175 self.strip_definite_mark(mark_index);
1176 }
1177
1178 n.visit_children_with(self);
1179 }
1180
1181 fn visit_array_pat(&mut self, n: &ArrayPat) {
1182 if n.optional {
1183 let mark_index = if let Some(type_ann) = &n.type_ann {
1184 self.get_prev_token_index(type_ann.span.lo - BytePos(1))
1185 } else {
1186 self.get_next_token_index(n.span.hi)
1187 };
1188 self.strip_optional_mark(mark_index);
1189 }
1190
1191 n.visit_children_with(self);
1192 }
1193
1194 fn visit_object_pat(&mut self, n: &ObjectPat) {
1195 if n.optional {
1196 let mark_index = if let Some(type_ann) = &n.type_ann {
1197 self.get_prev_token_index(type_ann.span.lo - BytePos(1))
1198 } else {
1199 self.get_next_token_index(n.span.hi)
1200 };
1201 self.strip_optional_mark(mark_index);
1202 }
1203
1204 n.visit_children_with(self);
1205 }
1206
1207 fn visit_export_all(&mut self, n: &ExportAll) {
1208 if n.type_only {
1209 self.add_replacement(n.span);
1210 self.fix_asi(n.span);
1211 return;
1212 }
1213
1214 n.visit_children_with(self);
1215 }
1216
1217 fn visit_export_decl(&mut self, n: &ExportDecl) {
1218 if n.decl.is_ts_declare() || n.decl.is_uninstantiated() {
1219 self.add_replacement(n.span);
1220 self.fix_asi(n.span);
1221 return;
1222 }
1223
1224 n.visit_children_with(self);
1225 }
1226
1227 fn visit_export_default_decl(&mut self, n: &ExportDefaultDecl) {
1228 if n.decl.is_ts_declare() || n.decl.is_uninstantiated() {
1229 self.add_replacement(n.span);
1230 self.fix_asi(n.span);
1231 return;
1232 }
1233
1234 n.visit_children_with(self);
1235 }
1236
1237 fn visit_decl(&mut self, n: &Decl) {
1238 if n.is_ts_declare() || n.is_uninstantiated() {
1239 self.add_replacement(n.span());
1240 self.fix_asi(n.span());
1241 return;
1242 }
1243
1244 n.visit_children_with(self);
1245 }
1246
1247 fn visit_import_decl(&mut self, n: &ImportDecl) {
1248 if n.type_only {
1249 self.add_replacement(n.span);
1250 self.fix_asi(n.span);
1251 return;
1252 }
1253
1254 n.visit_children_with(self);
1255 }
1256
1257 fn visit_import_specifiers(&mut self, n: &[ImportSpecifier]) {
1258 for import in n {
1259 if let ImportSpecifier::Named(import) = import {
1260 if import.is_type_only {
1261 let mut span = import.span;
1262 let comma = self.get_next_token(import.span.hi);
1263 if comma.token == Token::Comma {
1264 span = span.with_hi(comma.span.hi);
1265 } else {
1266 debug_assert_eq!(comma.token, Token::RBrace);
1267 }
1268 self.add_replacement(span);
1269 }
1270 }
1271 }
1272 }
1273
1274 fn visit_named_export(&mut self, n: &NamedExport) {
1275 if n.type_only {
1276 self.add_replacement(n.span);
1277 self.fix_asi(n.span);
1278 return;
1279 }
1280
1281 for export in n.specifiers.iter() {
1282 if let ExportSpecifier::Named(e) = export {
1283 if e.is_type_only {
1284 let mut span = e.span;
1285 let comma = self.get_next_token(e.span.hi);
1286 if comma.token == Token::Comma {
1287 span = span.with_hi(comma.span.hi);
1288 } else {
1289 debug_assert_eq!(comma.token, Token::RBrace);
1290 }
1291 self.add_replacement(span);
1292 }
1293 }
1294 }
1295 }
1296
1297 fn visit_params(&mut self, n: &[Param]) {
1298 if let Some(p) = n.first().filter(|param| {
1299 matches!(
1300 ¶m.pat,
1301 Pat::Ident(id) if id.sym == "this"
1302 )
1303 }) {
1304 let mut span = p.span;
1305
1306 let comma = self.get_next_token(span.hi);
1307 if comma.token == Token::Comma {
1308 span = span.with_hi(comma.span.hi);
1309 } else {
1310 debug_assert_eq!(comma.token, Token::RParen);
1311 }
1312 self.add_replacement(span);
1313
1314 n[1..].visit_children_with(self);
1315
1316 return;
1317 }
1318
1319 n.visit_children_with(self);
1320 }
1321
1322 fn visit_ts_as_expr(&mut self, n: &TsAsExpr) {
1323 self.add_replacement(span(n.expr.span().hi, n.span.hi));
1324 let TokenAndSpan {
1325 token,
1326 span: as_span,
1327 ..
1328 } = self.get_next_token(n.expr.span_hi());
1329 debug_assert_eq!(token, &Token::As);
1330 self.fix_asi_in_expr(span(as_span.lo, n.span.hi));
1331
1332 n.expr.visit_children_with(self);
1333 }
1334
1335 fn visit_ts_const_assertion(&mut self, n: &TsConstAssertion) {
1336 self.add_replacement(span(n.expr.span().hi, n.span.hi));
1337
1338 n.expr.visit_children_with(self);
1339 }
1340
1341 fn visit_ts_export_assignment(&mut self, n: &TsExportAssignment) {
1342 if HANDLER.is_set() {
1343 HANDLER.with(|handler| {
1344 handler
1345 .struct_span_err(
1346 n.span,
1347 "TypeScript export assignment is not supported in strip-only mode",
1348 )
1349 .code(DiagnosticId::Error("UnsupportedSyntax".into()))
1350 .emit();
1351 });
1352 }
1353 }
1354
1355 fn visit_ts_import_equals_decl(&mut self, n: &TsImportEqualsDecl) {
1356 if n.is_type_only {
1357 self.add_replacement(n.span);
1358 self.fix_asi(n.span);
1359 return;
1360 }
1361
1362 if HANDLER.is_set() {
1363 HANDLER.with(|handler| {
1364 handler
1365 .struct_span_err(
1366 n.span,
1367 "TypeScript import equals declaration is not supported in strip-only mode",
1368 )
1369 .code(DiagnosticId::Error("UnsupportedSyntax".into()))
1370 .emit();
1371 });
1372 }
1373 }
1374
1375 fn visit_ts_index_signature(&mut self, n: &TsIndexSignature) {
1376 self.add_replacement(n.span);
1377 }
1378
1379 fn visit_ts_instantiation(&mut self, n: &TsInstantiation) {
1380 self.add_replacement(span(n.expr.span().hi, n.span.hi));
1381
1382 n.expr.visit_children_with(self);
1383 }
1384
1385 fn visit_ts_enum_decl(&mut self, e: &TsEnumDecl) {
1386 if HANDLER.is_set() {
1387 HANDLER.with(|handler| {
1388 handler
1389 .struct_span_err(
1390 e.span,
1391 "TypeScript enum is not supported in strip-only mode",
1392 )
1393 .code(DiagnosticId::Error("UnsupportedSyntax".into()))
1394 .emit();
1395 });
1396 }
1397 }
1398
1399 fn visit_ts_module_decl(&mut self, n: &TsModuleDecl) {
1400 if HANDLER.is_set() {
1401 HANDLER.with(|handler| {
1402 handler
1403 .struct_span_err(
1404 n.span(),
1405 "TypeScript namespace declaration is not supported in strip-only mode",
1406 )
1407 .code(DiagnosticId::Error("UnsupportedSyntax".into()))
1408 .emit();
1409 });
1410 }
1411 }
1412
1413 fn visit_ts_non_null_expr(&mut self, n: &TsNonNullExpr) {
1414 self.add_replacement(span(n.span.hi - BytePos(1), n.span.hi));
1415
1416 n.expr.visit_children_with(self);
1417 }
1418
1419 fn visit_ts_param_prop_param(&mut self, n: &TsParamPropParam) {
1420 if HANDLER.is_set() {
1421 HANDLER.with(|handler| {
1422 handler
1423 .struct_span_err(
1424 n.span(),
1425 "TypeScript parameter property is not supported in strip-only mode",
1426 )
1427 .code(DiagnosticId::Error("UnsupportedSyntax".into()))
1428 .emit();
1429 });
1430 }
1431 }
1432
1433 fn visit_ts_satisfies_expr(&mut self, n: &TsSatisfiesExpr) {
1434 self.add_replacement(span(n.expr.span().hi, n.span.hi));
1435
1436 let TokenAndSpan {
1437 token,
1438 span: as_span,
1439 ..
1440 } = self.get_next_token(n.expr.span_hi());
1441 debug_assert_eq!(token, &Token::Satisfies);
1442 self.fix_asi_in_expr(span(as_span.lo, n.span.hi));
1443
1444 n.expr.visit_children_with(self);
1445 }
1446
1447 fn visit_ts_type_alias_decl(&mut self, n: &TsTypeAliasDecl) {
1448 self.add_replacement(n.span);
1449 self.fix_asi(n.span);
1450 }
1451
1452 fn visit_ts_type_ann(&mut self, n: &TsTypeAnn) {
1453 self.add_replacement(n.span);
1454 }
1455
1456 fn visit_ts_type_assertion(&mut self, n: &TsTypeAssertion) {
1460 if HANDLER.is_set() {
1461 HANDLER.with(|handler| {
1462 handler
1463 .struct_span_err(
1464 n.span,
1465 "The angle-bracket syntax for type assertions, `<T>expr`, is not \
1466 supported in type strip mode. Instead, use the 'as' syntax: `expr as T`.",
1467 )
1468 .code(DiagnosticId::Error("UnsupportedSyntax".into()))
1469 .emit();
1470 });
1471 }
1472
1473 n.expr.visit_children_with(self);
1474 }
1475
1476 fn visit_ts_type_param_decl(&mut self, n: &TsTypeParamDecl) {
1477 self.add_replacement(n.span);
1478 }
1479
1480 fn visit_ts_type_param_instantiation(&mut self, n: &TsTypeParamInstantiation) {
1481 self.add_replacement(span(n.span.lo, n.span.hi));
1482 }
1483
1484 fn visit_if_stmt(&mut self, n: &IfStmt) {
1485 n.visit_children_with(self);
1486
1487 if n.cons.is_ts_declare() {
1488 self.add_overwrite(n.cons.span_lo(), b';');
1489 }
1490
1491 if let Some(alt) = &n.alt {
1492 if alt.is_ts_declare() {
1493 self.add_overwrite(alt.span_lo(), b';');
1494 }
1495 }
1496 }
1497
1498 fn visit_for_stmt(&mut self, n: &ForStmt) {
1499 n.visit_children_with(self);
1500
1501 if n.body.is_ts_declare() {
1502 self.add_overwrite(n.body.span_lo(), b';');
1503 }
1504 }
1505
1506 fn visit_for_in_stmt(&mut self, n: &ForInStmt) {
1507 n.visit_children_with(self);
1508
1509 if n.body.is_ts_declare() {
1510 self.add_overwrite(n.body.span_lo(), b';');
1511 }
1512 }
1513
1514 fn visit_for_of_stmt(&mut self, n: &ForOfStmt) {
1515 n.visit_children_with(self);
1516
1517 if n.body.is_ts_declare() {
1518 self.add_overwrite(n.body.span_lo(), b';');
1519 }
1520 }
1521
1522 fn visit_while_stmt(&mut self, n: &WhileStmt) {
1523 n.visit_children_with(self);
1524
1525 if n.body.is_ts_declare() {
1526 self.add_overwrite(n.body.span_lo(), b';');
1527 }
1528 }
1529
1530 fn visit_do_while_stmt(&mut self, n: &DoWhileStmt) {
1531 n.visit_children_with(self);
1532
1533 if n.body.is_ts_declare() {
1534 self.add_overwrite(n.body.span_lo(), b';');
1535 }
1536 }
1537
1538 fn visit_getter_prop(&mut self, n: &GetterProp) {
1539 let l_parern_index = self.get_next_token_index(n.key.span_hi());
1540 let l_parern = &self.tokens[l_parern_index];
1541 debug_assert_eq!(l_parern.token, Token::LParen);
1542
1543 let r_parern_pos = n.type_ann.as_ref().map_or(n.body.span_lo(), |t| t.span.lo) - BytePos(1);
1544 let r_parern = self.get_prev_token(r_parern_pos);
1545 debug_assert_eq!(r_parern.token, Token::RParen);
1546
1547 let span = span(l_parern.span.lo + BytePos(1), r_parern.span.hi - BytePos(1));
1548 self.add_replacement(span);
1549
1550 n.visit_children_with(self);
1551 }
1552
1553 fn visit_setter_prop(&mut self, n: &SetterProp) {
1554 if let Some(this_param) = &n.this_param {
1555 self.add_replacement(this_param.span());
1556
1557 let comma = self.get_prev_token(n.param.span_lo() - BytePos(1));
1558 debug_assert_eq!(comma.token, Token::Comma);
1559
1560 self.add_replacement(comma.span);
1561 }
1562
1563 n.visit_children_with(self);
1564 }
1565}
1566
1567#[inline(always)]
1568fn is_new_line(c: char) -> bool {
1569 matches!(c, '\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}')
1570}
1571trait IsTsDecl {
1572 fn is_ts_declare(&self) -> bool;
1573}
1574
1575impl IsTsDecl for Decl {
1576 fn is_ts_declare(&self) -> bool {
1577 match self {
1578 Self::TsInterface(..) | Self::TsTypeAlias(..) => true,
1579
1580 Self::TsModule(module) => module.declare || matches!(module.id, TsModuleName::Str(..)),
1581 Self::TsEnum(ref r#enum) => r#enum.declare,
1582
1583 Self::Var(ref var) => var.declare,
1584 Self::Fn(FnDecl { declare: true, .. })
1585 | Self::Class(ClassDecl { declare: true, .. }) => true,
1586
1587 Self::Fn(FnDecl { function, .. }) => function.body.is_none(),
1588
1589 _ => false,
1590 }
1591 }
1592}
1593
1594impl IsTsDecl for Stmt {
1595 fn is_ts_declare(&self) -> bool {
1596 self.as_decl().is_some_and(IsTsDecl::is_ts_declare)
1597 }
1598}
1599
1600impl IsTsDecl for DefaultDecl {
1601 fn is_ts_declare(&self) -> bool {
1602 match self {
1603 Self::Class(..) => false,
1604 Self::Fn(r#fn) => r#fn.function.body.is_none(),
1605 Self::TsInterfaceDecl(..) => true,
1606 #[cfg(swc_ast_unknown)]
1607 _ => panic!("unable to access unknown nodes"),
1608 }
1609 }
1610}
1611
1612trait IsUninstantiated {
1613 fn is_uninstantiated(&self) -> bool;
1614}
1615
1616impl IsUninstantiated for TsNamespaceBody {
1617 fn is_uninstantiated(&self) -> bool {
1618 match self {
1619 Self::TsModuleBlock(block) => {
1620 block.body.iter().all(IsUninstantiated::is_uninstantiated)
1621 }
1622 Self::TsNamespaceDecl(decl) => decl.body.is_uninstantiated(),
1623 #[cfg(swc_ast_unknown)]
1624 _ => panic!("unable to access unknown nodes"),
1625 }
1626 }
1627}
1628
1629impl IsUninstantiated for ModuleItem {
1630 fn is_uninstantiated(&self) -> bool {
1631 match self {
1632 Self::Stmt(stmt) => stmt.is_uninstantiated(),
1633 Self::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { decl, .. })) => {
1634 decl.is_uninstantiated()
1635 }
1636 _ => false,
1637 }
1638 }
1639}
1640
1641impl IsUninstantiated for Stmt {
1642 fn is_uninstantiated(&self) -> bool {
1643 matches!(self, Self::Decl(decl) if decl.is_uninstantiated())
1644 }
1645}
1646
1647impl IsUninstantiated for TsModuleDecl {
1648 fn is_uninstantiated(&self) -> bool {
1649 matches!(&self.body, Some(body) if body.is_uninstantiated())
1650 }
1651}
1652
1653impl IsUninstantiated for Decl {
1654 fn is_uninstantiated(&self) -> bool {
1655 match self {
1656 Self::TsInterface(..) | Self::TsTypeAlias(..) => true,
1657 Self::TsModule(module) => module.is_uninstantiated(),
1658 _ => false,
1659 }
1660 }
1661}
1662
1663impl IsUninstantiated for DefaultDecl {
1664 fn is_uninstantiated(&self) -> bool {
1665 matches!(self, Self::TsInterfaceDecl(..))
1666 }
1667}
1668
1669trait U8Helper {
1670 fn is_utf8_char_boundary(&self) -> bool;
1671}
1672
1673impl U8Helper for u8 {
1674 #[inline]
1676 fn is_utf8_char_boundary(&self) -> bool {
1677 (*self as i8) >= -0x40
1679 }
1680}
1681
1682fn span(lo: BytePos, hi: BytePos) -> Span {
1683 Span::new(lo, hi)
1684}