@ 261027.7

261020.1 · 1d7e0126f3e601103d93586a6f872144f2789dad

chore: self-maintain Node.js features

reading declarations

Changes to deps/swc/bindings/binding_nodejs_support_wasm/src/. All files

deps/swc/bindings/binding_nodejs_support_wasm/src/error_reporter.rs

deleted file mode 100644@@ -1,881 +0,0 @@-use std::fmt::{self};--use miette::{-    Diagnostic, GraphicalTheme, LabeledSpan, ReportHandler, SourceCode, SourceSpan, ThemeCharacters,-};-use owo_colors::{OwoColorize, Style};-use unicode_width::UnicodeWidthChar;--/**-A [`ReportHandler`] that displays a given [`Report`](crate::Report) in a-quasi-graphical way, using terminal colors, unicode drawing characters, and-other such things.--This is the default reporter bundled with `miette`.--This printer can be customized by using [`new_themed()`](GraphicalReportHandler::new_themed) and handing it a-[`GraphicalTheme`] of your own creation (or using one of its own defaults!)--See [`set_hook()`](crate::set_hook) for more details on customizing your global-printer.-*/-#[derive(Debug, Clone)]-pub struct SwcReportHandler {-    pub(crate) theme: GraphicalTheme,-    pub(crate) context_lines: usize,-    pub(crate) tab_width: usize,-}--impl SwcReportHandler {-    /// Create a new `GraphicalReportHandler` with the default-    /// [`GraphicalTheme`]. This will use both unicode characters and colors.-    pub fn new() -> Self {-        Self {-            theme: GraphicalTheme::default(),-            context_lines: 1,-            tab_width: 4,-        }-    }--    /// Set a theme for this handler.-    pub fn with_theme(mut self, theme: GraphicalTheme) -> Self {-        self.theme = theme;-        self-    }-}--impl Default for SwcReportHandler {-    fn default() -> Self {-        Self::new()-    }-}--impl SwcReportHandler {-    /// Render a [`Diagnostic`]. This function is mostly internal and meant to-    /// be called by the toplevel [`ReportHandler`] handler, but is made public-    /// to make it easier (possible) to test in isolation from global state.-    pub fn render_report(-        &self,-        f: &mut impl fmt::Write,-        diagnostic: &dyn Diagnostic,-    ) -> fmt::Result {-        self.render_report_inner(f, diagnostic, diagnostic.source_code())-    }--    fn render_report_inner(-        &self,-        f: &mut impl fmt::Write,-        diagnostic: &dyn Diagnostic,-        parent_src: Option<&dyn SourceCode>,-    ) -> fmt::Result {-        let src = diagnostic.source_code().or(parent_src);-        self.render_snippets(f, diagnostic, src)?;--        Ok(())-    }--    fn render_snippets(-        &self,-        f: &mut impl fmt::Write,-        diagnostic: &dyn Diagnostic,-        opt_source: Option<&dyn SourceCode>,-    ) -> fmt::Result {-        let source = match opt_source {-            Some(source) => source,-            None => return Ok(()),-        };-        let labels = match diagnostic.labels() {-            Some(labels) => labels,-            None => return Ok(()),-        };--        let mut labels = labels.collect::<Vec<_>>();-        labels.sort_unstable_by_key(|l| l.inner().offset());--        let mut contexts = Vec::with_capacity(labels.len());-        for right in labels.iter().cloned() {-            let right_conts =-                match source.read_span(right.inner(), self.context_lines, self.context_lines) {-                    Ok(cont) => cont,-                    Err(err) => {-                        writeln!(-                            f,-                            "  [{} `{}` (offset: {}, length: {}): {:?}]",-                            "Failed to read contents for label".style(self.theme.styles.error),-                            right-                                .label()-                                .unwrap_or("<none>")-                                .style(self.theme.styles.link),-                            right.offset().style(self.theme.styles.link),-                            right.len().style(self.theme.styles.link),-                            err.style(self.theme.styles.warning)-                        )?;-                        return Ok(());-                    }-                };--            if contexts.is_empty() {-                contexts.push((right, right_conts));-                continue;-            }--            let (left, left_conts) = contexts.last().unwrap();-            if left_conts.line() + left_conts.line_count() >= right_conts.line() {-                // The snippets will overlap, so we create one Big Chunky Boi-                let left_end = left.offset() + left.len();-                let right_end = right.offset() + right.len();-                let new_end = std::cmp::max(left_end, right_end);--                let new_span = LabeledSpan::new(-                    left.label().map(String::from),-                    left.offset(),-                    new_end - left.offset(),-                );-                // Check that the two contexts can be combined-                if let Ok(new_conts) =-                    source.read_span(new_span.inner(), self.context_lines, self.context_lines)-                {-                    contexts.pop();-                    // We'll throw the contents away later-                    contexts.push((new_span, new_conts));-                    continue;-                }-            }--            contexts.push((right, right_conts));-        }-        for (ctx, _) in contexts {-            self.render_context(f, source, &ctx, &labels[..])?;-        }--        Ok(())-    }--    fn render_context(-        &self,-        f: &mut impl fmt::Write,-        source: &dyn SourceCode,-        context: &LabeledSpan,-        labels: &[LabeledSpan],-    ) -> fmt::Result {-        let lines = self.get_lines(source, context.inner())?;--        // sorting is your friend-        let labels = labels-            .iter()-            .zip(self.theme.styles.highlights.iter().cloned().cycle())-            .map(|(label, st)| FancySpan::new(label.label().map(String::from), *label.inner(), st))-            .collect::<Vec<_>>();--        // The max number of gutter-lines that will be active at any given-        // point. We need this to figure out indentation, so we do one loop-        // over the lines to see what the damage is gonna be.-        let mut max_gutter = 0usize;-        for line in &lines {-            let mut num_highlights = 0;-            for hl in &labels {-                if !line.span_line_only(hl) && line.span_applies_gutter(hl) {-                    num_highlights += 1;-                }-            }-            max_gutter = std::cmp::max(max_gutter, num_highlights);-        }--        // Now it's time for the fun part--actually rendering everything!-        for line in &lines {-            // Then, we need to print the gutter, along with any fly-bys We-            // have separate gutters depending on whether we're on the actual-            // line, or on one of the "highlight lines" below it.-            self.render_line_gutter(f, max_gutter, line, &labels)?;--            // And _now_ we can print out the line text itself!-            self.render_line_text(f, &line.text)?;--            // Next, we write all the highlights that apply to this particular line.-            let (single_line, multi_line): (Vec<_>, Vec<_>) = labels-                .iter()-                .filter(|hl| line.span_applies(hl))-                .partition(|hl| line.span_line_only(hl));-            if !single_line.is_empty() {-                // gutter _again_-                self.render_highlight_gutter(-                    f,-                    max_gutter,-                    line,-                    &labels,-                    LabelRenderMode::SingleLine,-                )?;-                self.render_single_line_highlights(f, line, max_gutter, &single_line, &labels)?;-            }-            for hl in multi_line {-                if hl.label().is_some() && line.span_ends(hl) && !line.span_starts(hl) {-                    self.render_multi_line_end(f, &labels, max_gutter, line, hl)?;-                }-            }-        }--        Ok(())-    }--    fn render_multi_line_end(-        &self,-        f: &mut impl fmt::Write,-        labels: &[FancySpan],-        max_gutter: usize,-        line: &Line,-        label: &FancySpan,-    ) -> fmt::Result {-        if let Some(label_parts) = label.label_parts() {-            // if it has a label, how long is it?-            let (first, rest) = label_parts.split_first().expect(-                "cannot crash because rest would have been None, see docs on the `label` field of \-                 FancySpan",-            );--            if rest.is_empty() {-                // gutter _again_-                self.render_highlight_gutter(-                    f,-                    max_gutter,-                    line,-                    labels,-                    LabelRenderMode::SingleLine,-                )?;--                self.render_multi_line_end_single(-                    f,-                    first,-                    label.style,-                    LabelRenderMode::SingleLine,-                )?;-            } else {-                // gutter _again_-                self.render_highlight_gutter(-                    f,-                    max_gutter,-                    line,-                    labels,-                    LabelRenderMode::MultiLineFirst,-                )?;--                self.render_multi_line_end_single(-                    f,-                    first,-                    label.style,-                    LabelRenderMode::MultiLineFirst,-                )?;-                for label_line in rest {-                    // gutter _again_-                    self.render_highlight_gutter(-                        f,-                        max_gutter,-                        line,-                        labels,-                        LabelRenderMode::MultiLineRest,-                    )?;-                    self.render_multi_line_end_single(-                        f,-                        label_line,-                        label.style,-                        LabelRenderMode::MultiLineRest,-                    )?;-                }-            }-        } else {-            // gutter _again_-            self.render_highlight_gutter(f, max_gutter, line, labels, LabelRenderMode::SingleLine)?;-            // has no label-            writeln!(f, "{}", self.theme.characters.hbar.style(label.style))?;-        }--        Ok(())-    }--    fn render_line_gutter(-        &self,-        f: &mut impl fmt::Write,-        max_gutter: usize,-        line: &Line,-        highlights: &[FancySpan],-    ) -> fmt::Result {-        if max_gutter == 0 {-            return Ok(());-        }-        let chars = &self.theme.characters;-        let mut gutter = String::new();-        let applicable = highlights.iter().filter(|hl| line.span_applies_gutter(hl));-        let mut arrow = false;-        for (i, hl) in applicable.enumerate() {-            if line.span_starts(hl) {-                gutter.push_str(&chars.ltop.style(hl.style).to_string());-                gutter.push_str(-                    &chars-                        .hbar-                        .to_string()-                        .repeat(max_gutter.saturating_sub(i))-                        .style(hl.style)-                        .to_string(),-                );-                gutter.push_str(&chars.rarrow.style(hl.style).to_string());-                arrow = true;-                break;-            } else if line.span_ends(hl) {-                if hl.label().is_some() {-                    gutter.push_str(&chars.lcross.style(hl.style).to_string());-                } else {-                    gutter.push_str(&chars.lbot.style(hl.style).to_string());-                }-                gutter.push_str(-                    &chars-                        .hbar-                        .to_string()-                        .repeat(max_gutter.saturating_sub(i))-                        .style(hl.style)-                        .to_string(),-                );-                gutter.push_str(&chars.rarrow.style(hl.style).to_string());-                arrow = true;-                break;-            } else if line.span_flyby(hl) {-                gutter.push_str(&chars.vbar.style(hl.style).to_string());-            } else {-                gutter.push(' ');-            }-        }-        write!(-            f,-            "{}{}",-            gutter,-            " ".repeat(-                if arrow { 1 } else { 3 } + max_gutter.saturating_sub(gutter.chars().count())-            )-        )?;-        Ok(())-    }--    fn render_highlight_gutter(-        &self,-        f: &mut impl fmt::Write,-        max_gutter: usize,-        line: &Line,-        highlights: &[FancySpan],-        render_mode: LabelRenderMode,-    ) -> fmt::Result {-        if max_gutter == 0 {-            return Ok(());-        }--        // keeps track of how many columns wide the gutter is-        // important for ansi since simply measuring the size of the final string-        // gives the wrong result when the string contains ansi codes.-        let mut gutter_cols = 0;--        let chars = &self.theme.characters;-        let mut gutter = String::new();-        let applicable = highlights.iter().filter(|hl| line.span_applies_gutter(hl));-        for (i, hl) in applicable.enumerate() {-            if !line.span_line_only(hl) && line.span_ends(hl) {-                if render_mode == LabelRenderMode::MultiLineRest {-                    // this is to make multiline labels work. We want to make the right amount-                    // of horizontal space for them, but not actually draw the lines-                    let horizontal_space = max_gutter.saturating_sub(i) + 2;-                    for _ in 0..horizontal_space {-                        gutter.push(' ');-                    }-                    // account for one more horizontal space, since in multiline mode-                    // we also add in the vertical line before the label like this:-                    // 2 │ ╭─▶   text-                    // 3 │ ├─▶     here-                    //   · ╰──┤ these two lines-                    //   ·    │ are the problem-                    //        ^this-                    gutter_cols += horizontal_space + 1;-                } else {-                    let num_repeat = max_gutter.saturating_sub(i) + 2;--                    gutter.push_str(&chars.lbot.style(hl.style).to_string());--                    gutter.push_str(-                        &chars-                            .hbar-                            .to_string()-                            .repeat(-                                num_repeat-                                    // if we are rendering a multiline label, then leave a bit of space for the-                                    // rcross character-                                    - if render_mode == LabelRenderMode::MultiLineFirst {-                                        1-                                    } else {-                                        0-                                    },-                            )-                            .style(hl.style)-                            .to_string(),-                    );--                    // we count 1 for the lbot char, and then a few more, the same number-                    // as we just repeated for. For each repeat we only add 1, even though-                    // due to ansi escape codes the number of bytes in the string could grow-                    // a lot each time.-                    gutter_cols += num_repeat + 1;-                }-                break;-            } else {-                gutter.push_str(&chars.vbar.style(hl.style).to_string());--                // we may push many bytes for the ansi escape codes style adds,-                // but we still only add a single character-width to the string in a terminal-                gutter_cols += 1;-            }-        }--        // now calculate how many spaces to add based on how many columns we just-        // created. it's the max width of the gutter, minus how many-        // character-widths we just generated capped at 0 (though this should-        // never go below in reality), and then we add 3 to account for-        // arrowheads when a gutter line ends-        let num_spaces = (max_gutter + 3).saturating_sub(gutter_cols);-        // we then write the gutter and as many spaces as we need-        write!(f, "{}{:width$}", gutter, "", width = num_spaces)?;-        Ok(())-    }--    /// Returns an iterator over the visual width of each character in a line.-    fn line_visual_char_width<'a>(&self, text: &'a str) -> impl Iterator<Item = usize> + 'a {-        let mut column = 0;-        let mut escaped = false;-        let tab_width = self.tab_width;-        text.chars().map(move |c| {-            let width = match (escaped, c) {-                // Round up to the next multiple of tab_width-                (false, '\t') => tab_width - column % tab_width,-                // start of ANSI escape-                (false, '\x1b') => {-                    escaped = true;-                    0-                }-                // use Unicode width for all other characters-                (false, c) => c.width().unwrap_or(0),-                // end of ANSI escape-                (true, 'm') => {-                    escaped = false;-                    0-                }-                // characters are zero width within escape sequence-                (true, _) => 0,-            };-            column += width;-            width-        })-    }--    /// Returns the visual column position of a byte offset on a specific line.-    ///-    /// If the offset occurs in the middle of a character, the returned column-    /// corresponds to that character's first column in `start` is true, or its-    /// last column if `start` is false.-    fn visual_offset(&self, line: &Line, offset: usize, start: bool) -> usize {-        let line_range = line.offset..=(line.offset + line.length);-        assert!(line_range.contains(&offset));--        let mut text_index = offset - line.offset;-        while text_index <= line.text.len() && !line.text.is_char_boundary(text_index) {-            if start {-                text_index -= 1;-            } else {-                text_index += 1;-            }-        }-        let text = &line.text[..text_index.min(line.text.len())];-        let text_width = self.line_visual_char_width(text).sum();-        if text_index > line.text.len() {-            // Spans extending past the end of the line are always rendered as-            // one column past the end of the visible line.-            //-            // This doesn't necessarily correspond to a specific byte-offset,-            // since a span extending past the end of the line could contain:-            //  - an actual \n character (1 byte)-            //  - a CRLF (2 bytes)-            //  - EOF (0 bytes)-            text_width + 1-        } else {-            text_width-        }-    }--    /// Renders a line to the output formatter, replacing tabs with spaces.-    fn render_line_text(&self, f: &mut impl fmt::Write, text: &str) -> fmt::Result {-        for (c, width) in text.chars().zip(self.line_visual_char_width(text)) {-            if c == '\t' {-                for _ in 0..width {-                    f.write_char(' ')?;-                }-            } else {-                f.write_char(c)?;-            }-        }-        f.write_char('\n')?;-        Ok(())-    }--    fn render_single_line_highlights(-        &self,-        f: &mut impl fmt::Write,-        line: &Line,-        max_gutter: usize,-        single_liners: &[&FancySpan],-        all_highlights: &[FancySpan],-    ) -> fmt::Result {-        let mut underlines = String::new();-        let mut highest = 0;--        let chars = &self.theme.characters;-        let vbar_offsets: Vec<_> = single_liners-            .iter()-            .map(|hl| {-                let byte_start = hl.offset();-                let byte_end = hl.offset() + hl.len();-                let start = self.visual_offset(line, byte_start, true).max(highest);-                let end = if hl.len() == 0 {-                    start + 1-                } else {-                    self.visual_offset(line, byte_end, false).max(start + 1)-                };--                let vbar_offset = (start + end) / 2;-                let num_left = vbar_offset - start;-                let num_right = end - vbar_offset - 1;-                underlines.push_str(-                    &format!(-                        "{:width$}{}{}{}",-                        "",-                        chars.underline.to_string().repeat(num_left),-                        if hl.len() == 0 {-                            chars.uarrow-                        } else if hl.label().is_some() {-                            chars.underbar-                        } else {-                            chars.underline-                        },-                        chars.underline.to_string().repeat(num_right),-                        width = start.saturating_sub(highest),-                    )-                    .style(hl.style)-                    .to_string(),-                );-                highest = std::cmp::max(highest, end);--                (hl, vbar_offset)-            })-            .collect();-        writeln!(f, "{underlines}")?;--        for hl in single_liners.iter().rev() {-            if let Some(label) = hl.label_parts() {-                if label.len() == 1 {-                    self.write_label_text(-                        f,-                        line,-                        max_gutter,-                        all_highlights,-                        chars,-                        &vbar_offsets,-                        hl,-                        &label[0],-                        LabelRenderMode::SingleLine,-                    )?;-                } else {-                    let mut first = true;-                    for label_line in &label {-                        self.write_label_text(-                            f,-                            line,-                            max_gutter,-                            all_highlights,-                            chars,-                            &vbar_offsets,-                            hl,-                            label_line,-                            if first {-                                LabelRenderMode::MultiLineFirst-                            } else {-                                LabelRenderMode::MultiLineRest-                            },-                        )?;-                        first = false;-                    }-                }-            }-        }-        Ok(())-    }--    // I know it's not good practice, but making this a function makes a lot of-    // sense and making a struct for this does not...-    #[allow(clippy::too_many_arguments)]-    fn write_label_text(-        &self,-        f: &mut impl fmt::Write,-        line: &Line,-        max_gutter: usize,-        all_highlights: &[FancySpan],-        chars: &ThemeCharacters,-        vbar_offsets: &[(&&FancySpan, usize)],-        hl: &&FancySpan,-        label: &str,-        render_mode: LabelRenderMode,-    ) -> fmt::Result {-        self.render_highlight_gutter(-            f,-            max_gutter,-            line,-            all_highlights,-            LabelRenderMode::SingleLine,-        )?;-        let mut curr_offset = 1usize;-        for (offset_hl, vbar_offset) in vbar_offsets {-            while curr_offset < *vbar_offset + 1 {-                write!(f, " ")?;-                curr_offset += 1;-            }-            if *offset_hl != hl {-                write!(f, "{}", chars.vbar.to_string().style(offset_hl.style))?;-                curr_offset += 1;-            } else {-                let lines = match render_mode {-                    LabelRenderMode::SingleLine => format!(-                        "{}{} {}",-                        chars.lbot,-                        chars.hbar.to_string().repeat(2),-                        label,-                    ),-                    LabelRenderMode::MultiLineFirst => {-                        format!("{}{}{} {}", chars.lbot, chars.hbar, chars.rcross, label,)-                    }-                    LabelRenderMode::MultiLineRest => {-                        format!("  {} {}", chars.vbar, label,)-                    }-                };-                writeln!(f, "{}", lines.style(hl.style))?;-                break;-            }-        }-        Ok(())-    }--    fn render_multi_line_end_single(-        &self,-        f: &mut impl fmt::Write,-        label: &str,-        style: Style,-        render_mode: LabelRenderMode,-    ) -> fmt::Result {-        match render_mode {-            LabelRenderMode::SingleLine => {-                writeln!(f, "{} {}", self.theme.characters.hbar.style(style), label)?;-            }-            LabelRenderMode::MultiLineFirst => {-                writeln!(f, "{} {}", self.theme.characters.rcross.style(style), label)?;-            }-            LabelRenderMode::MultiLineRest => {-                writeln!(f, "{} {}", self.theme.characters.vbar.style(style), label)?;-            }-        }--        Ok(())-    }--    fn get_lines<'a>(-        &'a self,-        source: &'a dyn SourceCode,-        context_span: &'a SourceSpan,-    ) -> Result<Vec<Line>, fmt::Error> {-        let context_data = source-            .read_span(context_span, self.context_lines, self.context_lines)-            .map_err(|_| fmt::Error)?;-        let context = String::from_utf8_lossy(context_data.data());-        let mut column = context_data.column();-        let mut offset = context_data.span().offset();-        let mut line_offset = offset;-        let mut line_str = String::with_capacity(context.len());-        let mut lines = Vec::with_capacity(1);-        let mut iter = context.chars().peekable();-        while let Some(ch) = iter.next() {-            offset += ch.len_utf8();-            match ch {-                '\r' if iter.next_if_eq(&'\n').is_some() => {-                    offset += 1;-                    column = 0;-                }-                '\r' => {-                    line_str.push(ch);-                    column += 1;-                }-                '\n' => {-                    column = 0;-                }-                _ => {-                    line_str.push(ch);-                    column += 1;-                }-            }--            if column == 0 || iter.peek().is_none() {-                lines.push(Line {-                    offset: line_offset,-                    length: offset - line_offset,-                    text: line_str.clone(),-                });-                line_str.clear();-                line_offset = offset;-            }-        }-        Ok(lines)-    }-}--impl ReportHandler for SwcReportHandler {-    fn debug(&self, diagnostic: &dyn Diagnostic, f: &mut fmt::Formatter<'_>) -> fmt::Result {-        if f.alternate() {-            return fmt::Debug::fmt(diagnostic, f);-        }--        self.render_report(f, diagnostic)-    }-}--/*-Support types-*/--#[derive(PartialEq, Debug)]-enum LabelRenderMode {-    /// we're rendering a single line label (or not rendering in any special-    /// way)-    SingleLine,-    /// we're rendering a multiline label-    MultiLineFirst,-    /// we're rendering the rest of a multiline label-    MultiLineRest,-}--#[derive(Debug)]-struct Line {-    offset: usize,-    length: usize,-    text: String,-}--impl Line {-    fn span_line_only(&self, span: &FancySpan) -> bool {-        span.offset() >= self.offset && span.offset() + span.len() <= self.offset + self.length-    }--    /// Returns whether `span` should be visible on this line, either in the-    /// gutter or under the text on this line-    fn span_applies(&self, span: &FancySpan) -> bool {-        let spanlen = if span.len() == 0 { 1 } else { span.len() };-        // Span starts in this line--        (span.offset() >= self.offset && span.offset() < self.offset + self.length)-            // Span passes through this line-            || (span.offset() < self.offset && span.offset() + spanlen > self.offset + self.length) //todo-            // Span ends on this line-            || (span.offset() + spanlen > self.offset && span.offset() + spanlen <= self.offset + self.length)-    }--    /// Returns whether `span` should be visible on this line in the gutter (so-    /// this excludes spans that are only visible on this line and do not-    /// span multiple lines)-    fn span_applies_gutter(&self, span: &FancySpan) -> bool {-        let spanlen = if span.len() == 0 { 1 } else { span.len() };-        // Span starts in this line-        self.span_applies(span)-            && !(-                // as long as it doesn't start *and* end on this line-                (span.offset() >= self.offset && span.offset() < self.offset + self.length)-                    && (span.offset() + spanlen > self.offset-                        && span.offset() + spanlen <= self.offset + self.length)-            )-    }--    // A 'flyby' is a multi-line span that technically covers this line, but-    // does not begin or end within the line itself. This method is used to-    // calculate gutters.-    fn span_flyby(&self, span: &FancySpan) -> bool {-        // The span itself starts before this line's starting offset (so, in a-        // prev line).-        span.offset() < self.offset-            // ...and it stops after this line's end.-            && span.offset() + span.len() > self.offset + self.length-    }--    // Does this line contain the *beginning* of this multiline span?-    // This assumes self.span_applies() is true already.-    fn span_starts(&self, span: &FancySpan) -> bool {-        span.offset() >= self.offset-    }--    // Does this line contain the *end* of this multiline span?-    // This assumes self.span_applies() is true already.-    fn span_ends(&self, span: &FancySpan) -> bool {-        span.offset() + span.len() >= self.offset-            && span.offset() + span.len() <= self.offset + self.length-    }-}--#[derive(Debug, Clone)]-struct FancySpan {-    /// this is deliberately an option of a vec because I wanted to be very-    /// explicit that there can also be *no* label. If there is a label, it-    /// can have multiple lines which is what the vec is for.-    label: Option<Vec<String>>,-    span: SourceSpan,-    style: Style,-}--impl PartialEq for FancySpan {-    fn eq(&self, other: &Self) -> bool {-        self.label == other.label && self.span == other.span-    }-}--fn split_label(v: String) -> Vec<String> {-    v.split('\n').map(|i| i.to_string()).collect()-}--impl FancySpan {-    fn new(label: Option<String>, span: SourceSpan, style: Style) -> Self {-        FancySpan {-            label: label.map(split_label),-            span,-            style,-        }-    }--    fn style(&self) -> Style {-        self.style-    }--    fn label(&self) -> Option<String> {-        self.label-            .as_ref()-            .map(|l| l.join("\n").style(self.style()).to_string())-    }--    fn label_parts(&self) -> Option<Vec<String>> {-        self.label.as_ref().map(|l| {-            l.iter()-                .map(|i| i.style(self.style()).to_string())-                .collect()-        })-    }--    fn offset(&self) -> usize {-        self.span.offset()-    }--    fn len(&self) -> usize {-        self.span.len()-    }-}

deps/swc/bindings/binding_nodejs_support_wasm/src/lib.rs

deleted file mode 100644@@ -1,345 +0,0 @@-use std::{-    iter::once,-    mem::take,-    sync::{Arc, Mutex},-};--use anyhow::Error;-use error_reporter::SwcReportHandler;-use js_sys::Uint8Array;-use miette::{GraphicalTheme, LabeledSpan, ThemeCharacters, ThemeStyles};-use serde::Serialize;-use swc_common::{-    errors::{DiagnosticBuilder, DiagnosticId, Emitter, Handler, HANDLER},-    sync::Lrc,-    SourceMap, Span, GLOBALS,-};-use swc_error_reporters::{convert_span, to_pretty_source_code};-use swc_ts_fast_strip::{Options, TransformOutput};-use wasm_bindgen::prelude::*;-use wasm_bindgen_futures::{future_to_promise, js_sys::Promise};--mod error_reporter;-mod nodejs;--/// Custom interface definitions for `@swc/nodejs-support-wasm`.-#[wasm_bindgen(typescript_custom_section)]-const INTERFACE_DEFINITIONS: &'static str = r#"-export declare function transform(src: string | Uint8Array, opts?: Options): Promise<TransformOutput>;-export declare function transformSync(src: string | Uint8Array, opts?: Options): TransformOutput;-export declare function transformModuleSyntax(src: string | Uint8Array): ModuleSyntaxTransformOutput;-export declare function getFirstExpression(src: string | Uint8Array, startColumn: number): string;-export declare function isValidSyntax(src: string | Uint8Array): boolean;-export declare function isRecoverableError(src: string | Uint8Array): boolean;-export declare function tokenize(src: string | Uint8Array): Token[];-export declare function findTopLevelAwaits(src: string | Uint8Array): TopLevelAwaitLocation[];-export type { Options, TransformOutput };--export type TokenKind = "keyword" | "identifier" | "number" | "bigint" | "string" | "template" | "regexp" | "comment" | "punctuator";--export interface Token {-    kind: TokenKind;-    /** Zero-based UTF-16 offset, inclusive. */-    start: number;-    /** Zero-based UTF-16 offset, exclusive. */-    end: number;-}--export interface TopLevelAwaitLocation {-    /** One-based line number. */-    line: number;-    /** Zero-based UTF-16 column. */-    column: number;-}--export interface ModuleSyntaxTransformOutput {-    code: string;-    hadModuleSyntax: boolean;-}-"#;--#[wasm_bindgen(skip_typescript)]-pub fn transform(input: JsValue, options: JsValue) -> Promise {-    future_to_promise(async move { transform_sync(input, options) })-}--#[wasm_bindgen(js_name = "transformSync", skip_typescript)]-pub fn transform_sync(input: JsValue, options: JsValue) -> Result<JsValue, JsValue> {-    let options: Options = if options.is_falsy() {-        Default::default()-    } else {-        serde_wasm_bindgen::from_value(options)?-    };--    let input = coerce_input(input)?;--    let result = GLOBALS.set(&Default::default(), || operate(input, options));--    match result {-        Ok(v) => Ok(serde_wasm_bindgen::to_value(&v)?),-        Err(errors) => Err(serde_wasm_bindgen::to_value(&errors[0])?),-    }-}--#[wasm_bindgen(js_name = "transformModuleSyntax", skip_typescript)]-pub fn transform_module_syntax(input: JsValue) -> Result<JsValue, JsValue> {-    let input = coerce_input(input)?;-    let output = nodejs::transform_module_syntax(input);--    Ok(serde_wasm_bindgen::to_value(&output)?)-}--#[wasm_bindgen(js_name = "getFirstExpression", skip_typescript)]-pub fn get_first_expression(input: JsValue, start_column: u32) -> Result<String, JsValue> {-    let input = coerce_input(input)?;--    Ok(nodejs::get_first_expression(input, start_column))-}--#[wasm_bindgen(js_name = "isValidSyntax", skip_typescript)]-pub fn is_valid_syntax(input: JsValue) -> Result<bool, JsValue> {-    let input = coerce_input(input)?;--    Ok(nodejs::is_valid_syntax(input))-}--#[wasm_bindgen(js_name = "isRecoverableError", skip_typescript)]-pub fn is_recoverable_error(input: JsValue) -> Result<bool, JsValue> {-    let input = coerce_input(input)?;--    Ok(nodejs::is_recoverable_error(input))-}--/// Returns compact tokens for syntax highlighting, including a partial prefix-/// when the input is incomplete or invalid.-#[wasm_bindgen(skip_typescript)]-pub fn tokenize(input: JsValue) -> Result<JsValue, JsValue> {-    let input = coerce_input(input)?;-    Ok(serde_wasm_bindgen::to_value(&nodejs::tokenize(&input))?)-}--/// Returns module-scope await locations in source order using Acorn-/// coordinates.-#[wasm_bindgen(js_name = "findTopLevelAwaits", skip_typescript)]-pub fn find_top_level_awaits(input: JsValue) -> Result<JsValue, JsValue> {-    let input = coerce_input(input)?;-    Ok(serde_wasm_bindgen::to_value(-        &nodejs::find_top_level_awaits(&input),-    )?)-}--fn coerce_input(input: JsValue) -> Result<String, JsValue> {-    match input.as_string() {-        Some(input) => Ok(input),-        None => {-            if input.is_instance_of::<Uint8Array>() {-                let input = input.unchecked_into::<Uint8Array>();--                String::from_utf8(input.to_vec())-                    .map_err(|_| JsValue::from_str("Input Uint8Array is not valid utf-8"))-            } else {-                Err(JsValue::from_str("Input is not a string or Uint8Array"))-            }-        }-    }-}--fn operate(input: String, options: Options) -> Result<TransformOutput, Vec<JsonDiagnostic>> {-    let cm = Lrc::new(SourceMap::default());--    try_with_json_handler(cm.clone(), |handler| {-        swc_ts_fast_strip::operate(&cm, handler, input, options).map_err(anyhow::Error::new)-    })-}--#[derive(Clone)]-struct JsonErrorWriter {-    errors: Arc<Mutex<Vec<JsonDiagnostic>>>,-    /// Successful transforms never need the graphical diagnostic reporter.-    reporter: Option<SwcReportHandler>,-    cm: Lrc<SourceMap>,-}--fn try_with_json_handler<F, Ret>(cm: Lrc<SourceMap>, op: F) -> Result<Ret, Vec<JsonDiagnostic>>-where-    F: FnOnce(&Handler) -> Result<Ret, Error>,-{-    let wr = JsonErrorWriter {-        errors: Default::default(),-        reporter: None,-        cm,-    };-    let emitter: Box<dyn Emitter> = Box::new(wr.clone());--    let handler = Handler::with_emitter(true, false, emitter);--    let ret = HANDLER.set(&handler, || op(&handler));--    if handler.has_errors() {-        let mut lock = wr.errors.lock().unwrap();-        let error = take(&mut *lock);--        Err(error)-    } else {-        Ok(ret.expect("it should not fail without emitting errors to handler"))-    }-}--impl Emitter for JsonErrorWriter {-    fn emit(&mut self, db: &mut DiagnosticBuilder) {-        let d = &**db;--        let snippet = d.span.primary_span().and_then(|span| {-            let mut snippet = String::new();-            let reporter = self.reporter.get_or_insert_with(json_reporter);-            match reporter.render_report(-                &mut snippet,-                &Snippet {-                    source_code: &to_pretty_source_code(&self.cm, true),-                    span,-                },-            ) {-                Ok(()) => Some(snippet),-                Err(_) => None,-            }-        });--        let children = d-            .children-            .iter()-            .map(|d| todo!("json subdiagnostic: {d:?}"))-            .collect::<Vec<_>>();--        let error_code = match &d.code {-            Some(DiagnosticId::Error(s)) => Some(&**s),-            Some(DiagnosticId::Lint(s)) => Some(&**s),-            None => None,-        };--        let start = d-            .span-            .primary_span()-            .and_then(|span| self.cm.try_lookup_char_pos(span.lo()).ok());--        let end = d-            .span-            .primary_span()-            .and_then(|span| self.cm.try_lookup_char_pos(span.hi()).ok());--        let filename = start.as_ref().map(|loc| loc.file.name.to_string());--        let error = JsonDiagnostic {-            code: error_code.map(|s| s.to_string()),-            message: d.message[0].0.to_string(),-            snippet,-            filename,-            start_line: start.as_ref().map(|loc| loc.line),-            start_column: start.as_ref().map(|loc| loc.col_display),-            end_line: end.as_ref().map(|loc| loc.line),-            end_column: end.as_ref().map(|loc| loc.col_display),-            children,-        };--        self.errors.lock().unwrap().push(error);-    }--    fn take_diagnostics(&mut self) -> Vec<String> {-        Default::default()-    }-}--fn json_reporter() -> SwcReportHandler {-    SwcReportHandler::default().with_theme(GraphicalTheme {-        characters: ThemeCharacters {-            hbar: ' ',-            vbar: ' ',-            xbar: ' ',-            vbar_break: ' ',-            ltop: ' ',-            rtop: ' ',-            mtop: ' ',-            lbot: ' ',-            rbot: ' ',-            mbot: ' ',-            error: "".into(),-            warning: "".into(),-            advice: "".into(),-            ..ThemeCharacters::ascii()-        },-        styles: ThemeStyles::none(),-    })-}--#[derive(Serialize)]-#[serde(rename_all = "camelCase")]-struct JsonDiagnostic {-    /// Error code-    #[serde(skip_serializing_if = "Option::is_none")]-    code: Option<String>,-    message: String,--    #[serde(skip_serializing_if = "Option::is_none")]-    snippet: Option<String>,--    #[serde(skip_serializing_if = "Option::is_none")]-    filename: Option<String>,--    #[serde(skip_serializing_if = "Option::is_none")]-    start_line: Option<usize>,-    #[serde(skip_serializing_if = "Option::is_none")]-    start_column: Option<usize>,--    #[serde(skip_serializing_if = "Option::is_none")]-    end_line: Option<usize>,-    #[serde(skip_serializing_if = "Option::is_none")]-    end_column: Option<usize>,--    #[serde(skip_serializing_if = "Vec::is_empty")]-    children: Vec<JsonSubdiagnostic>,-}--#[derive(Serialize)]-#[serde(rename_all = "camelCase")]-struct JsonSubdiagnostic {-    message: String,-    #[serde(skip_serializing_if = "Option::is_none")]-    snippet: Option<String>,-    filename: String,-    line: usize,-}--struct Snippet<'a> {-    source_code: &'a dyn miette::SourceCode,-    span: Span,-}--impl miette::Diagnostic for Snippet<'_> {-    fn source_code(&self) -> Option<&dyn miette::SourceCode> {-        if self.span.lo().is_dummy() || self.span.hi().is_dummy() {-            return None;-        }--        Some(self.source_code)-    }--    fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {-        Some(Box::new(once(LabeledSpan::new_with_span(-            None,-            convert_span(self.span),-        ))))-    }-}--impl std::error::Error for Snippet<'_> {}--impl std::fmt::Display for Snippet<'_> {-    fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {-        Ok(())-    }-}--impl std::fmt::Debug for Snippet<'_> {-    fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {-        Ok(())-    }-}

deps/swc/bindings/binding_nodejs_support_wasm/src/nodejs.rs

deleted file mode 100644@@ -1,1369 +0,0 @@-//! Node.js-specific helpers for `@swc/nodejs-support-wasm`.-//!-//! These functions intentionally expose only the compact operations Node needs-//! from Rust and avoid serializing SWC ASTs into JavaScript.--use serde::Serialize;-use swc_common::{comments::SingleThreadedComments, sync::Lrc, FileName, SourceMap, Span, Spanned};-use swc_ecma_ast::{-    CallExpr, Callee, Decl, DefaultDecl, EsVersion, ExportSpecifier, Expr, ImportDecl,-    ImportSpecifier, MetaPropExpr, MetaPropKind, Module, ModuleDecl, ModuleExportName, ModuleItem,-    NamedExport, ObjectLit,-};-use swc_ecma_parser::{-    error::{Error as ParseError, SyntaxError},-    lexer::Lexer,-    unstable::{Token, TokenAndSpan},-    EsSyntax, Parser, StringInput, Syntax, TsSyntax,-};-use swc_ecma_visit::{Visit, VisitWith};--mod awaits;-mod position;-mod tokenize;--#[cfg(test)]-mod fixtures;--pub use self::{awaits::find_top_level_awaits, tokenize::tokenize};--const DYNAMIC_IMPORT_NAME: &str = "__nodeREPLDynamicImport";--#[derive(Debug, Serialize)]-#[serde(rename_all = "camelCase")]-pub struct ModuleSyntaxTransformOutput {-    pub code: String,-    pub had_module_syntax: bool,-}--/// Rewrites Node REPL module syntax into script syntax.-///-/// The transform follows Node's edit-list approach: static imports become-/// awaited dynamic imports, dynamic `import(...)` is routed through Node's REPL-/// helper, and exports are unwrapped or dropped depending on whether they have-/// runtime declarations.-pub fn transform_module_syntax(code: String) -> ModuleSyntaxTransformOutput {-    if !code.contains("import") && !code.contains("export") {-        return ModuleSyntaxTransformOutput {-            code,-            had_module_syntax: false,-        };-    }--    let Some(module) = parse_module(&code) else {-        return ModuleSyntaxTransformOutput {-            code,-            had_module_syntax: false,-        };-    };--    let (edits, hoisted) = {-        let mut transform = ModuleSyntaxTransform::new(&code);--        for item in &module.body {-            transform.collect_module_item_edit(item);-        }--        let replaced_ranges = transform-            .edits-            .iter()-            .map(|edit| (edit.start, edit.end))-            .collect::<Vec<_>>();-        module.visit_with(&mut ModuleFeatureCollector {-            code: &code,-            edits: &mut transform.edits,-            replaced_ranges: &replaced_ranges,-        });--        if transform.edits.is_empty() && transform.hoisted.is_empty() {-            return ModuleSyntaxTransformOutput {-                code,-                had_module_syntax: false,-            };-        }--        (transform.edits, transform.hoisted)-    };--    ModuleSyntaxTransformOutput {-        code: prepend_hoisted_imports(apply_edits(code, edits), hoisted),-        had_module_syntax: true,-    }-}--/// Extracts the first source expression associated with a V8 error column.-///-/// `start_column` is interpreted as a JavaScript UTF-16 code-unit column to-/// match Node/V8 source locations. The implementation uses only lexer tokens-/// because the source line can be incomplete JavaScript.-pub fn get_first_expression(code: String, start_column: u32) -> String {-    let start_byte = utf16_column_to_byte_pos(&code, start_column);-    let mut last_token = None;-    let mut potential_expression_start_token = None;-    let mut first_member_access_name_token = None;-    let mut pending_optional_chain_name_token = None;-    let mut terminating_byte = None;-    let mut paren_level = 0u32;-    let mut member_bracket_depth = 0u32;--    for token in lex_tokens(&code) {-        if token.token == Token::Eof {-            break;-        }--        let Some((token_start, token_end)) = span_range(token.span) else {-            continue;-        };-        if token_start < start_byte {-            if token.token == Token::Semi {-                potential_expression_start_token = None;-                first_member_access_name_token = None;-                pending_optional_chain_name_token = None;-                member_bracket_depth = 0;-                last_token = None;-                continue;-            }--            if member_bracket_depth > 0 {-                match token.token {-                    Token::LBracket => member_bracket_depth += 1,-                    Token::RBracket => {-                        member_bracket_depth = member_bracket_depth.saturating_sub(1);-                    }-                    _ => {}-                }-                last_token = Some(token);-                continue;-            }--            if is_member_identifier_token(&code, &token)-                && first_member_access_name_token.is_none()-                && potential_expression_start_token.is_none()-            {-                potential_expression_start_token = Some(token);-            }--            if token.token == Token::QuestionMark-                && last_token-                    .as_ref()-                    .is_some_and(|last: &TokenAndSpan| is_member_identifier_token(&code, last))-            {-                pending_optional_chain_name_token = last_token;-                last_token = Some(token);-                continue;-            }--            if token.token == Token::Dot-                && last_token-                    .as_ref()-                    .is_some_and(|last: &TokenAndSpan| last.token == Token::QuestionMark)-            {-                if let Some(name_token) = pending_optional_chain_name_token.take() {-                    first_member_access_name_token.get_or_insert(name_token);-                    last_token = Some(token);-                    continue;-                }-            } else if pending_optional_chain_name_token.take().is_some() {-                first_member_access_name_token = None;-            }--            if token.token == Token::LBracket-                && last_token-                    .as_ref()-                    .is_some_and(|last: &TokenAndSpan| is_member_identifier_token(&code, last))-            {-                first_member_access_name_token-                    .get_or_insert(potential_expression_start_token.unwrap_or(last_token.unwrap()));-                member_bracket_depth = 1;-                last_token = Some(token);-                continue;-            }--            if is_member_access_token(token.token) {-                if last_token-                    .as_ref()-                    .is_some_and(|last: &TokenAndSpan| is_member_identifier_token(&code, last))-                {-                    first_member_access_name_token.get_or_insert(-                        potential_expression_start_token.unwrap_or(last_token.unwrap()),-                    );-                    last_token = Some(token);-                    continue;-                }-            } else if token.token != Token::LParen && !is_member_name_token(&code, &token) {-                potential_expression_start_token = None;-                first_member_access_name_token = None;-            }--            if token.token == Token::LParen && first_member_access_name_token.is_some() {-                paren_level += 1;-                last_token = Some(token);-                continue;-            }--            if token.token == Token::LParen-                && last_token-                    .as_ref()-                    .is_some_and(|last: &TokenAndSpan| is_member_identifier_token(&code, last))-            {-                first_member_access_name_token-                    .get_or_insert(potential_expression_start_token.unwrap_or(last_token.unwrap()));-                paren_level += 1;-                last_token = Some(token);-                continue;-            }--            last_token = Some(token);-            continue;-        }--        if token.token == Token::LParen-            && last_token-                .as_ref()-                .is_some_and(|last: &TokenAndSpan| is_member_identifier_token(&code, last))-        {-            first_member_access_name_token-                .get_or_insert(potential_expression_start_token.unwrap_or(last_token.unwrap()));-        }--        match token.token {-            Token::LParen => {-                paren_level += 1;-            }-            Token::RParen => {-                paren_level = paren_level.saturating_sub(1);-                if paren_level == 0 {-                    terminating_byte = Some(token_end);-                    break;-                }-            }-            Token::Semi => {-                terminating_byte = Some(token_start);-                break;-            }-            _ => {}-        }-    }--    let start = first_member_access_name_token-        .and_then(|token| span_range(token.span).map(|(start, _)| start))-        .unwrap_or(start_byte);-    let end = terminating_byte.unwrap_or(code.len());--    if start >= code.len()-        || start > end-        || !code.is_char_boundary(start)-        || !code.is_char_boundary(end)-    {-        return String::new();-    }--    code[start..end].to_string()-}--/// Returns true when the source parses as a program or expression.-pub fn is_valid_syntax(code: String) -> bool {-    parses_as_program(&code) || parses_as_program(&format!("_={code}"))-}--/// Returns true for parser errors that should keep a Node REPL input open.-pub fn is_recoverable_error(code: String) -> bool {-    match parse_program_result(&code) {-        ParseOutcome::Valid => false,-        ParseOutcome::Invalid(errors) => {-            if errors-                .iter()-                .any(|error| is_recoverable_parse_error(&code, error))-            {-                return true;-            }--            code.trim_start().starts_with('{') && is_recoverable_error(format!("({code}"))-        }-    }-}--struct ModuleSyntaxTransform<'a> {-    code: &'a str,-    edits: Vec<Edit>,-    hoisted: Vec<String>,-}--impl<'a> ModuleSyntaxTransform<'a> {-    fn new(code: &'a str) -> Self {-        Self {-            code,-            edits: Vec::new(),-            hoisted: Vec::new(),-        }-    }--    fn collect_module_item_edit(&mut self, item: &ModuleItem) {-        let ModuleItem::ModuleDecl(decl) = item else {-            return;-        };--        match decl {-            ModuleDecl::Import(import) => {-                self.delete_span(import.span);-                if !import.type_only {-                    if let Some(script) = self.import_decl_to_script(import) {-                        self.hoisted.push(script);-                    }-                }-            }-            ModuleDecl::ExportDecl(export) => {-                if is_type_only_decl(&export.decl) {-                    self.delete_span(export.span);-                } else if let (Some((start, _)), Some((decl_start, _))) =-                    (span_range(export.span), span_range(export.decl.span()))-                {-                    push_range_edit(&mut self.edits, self.code, start, decl_start, String::new());-                }-            }-            ModuleDecl::ExportNamed(export) => {-                self.delete_span(export.span);-                if let Some(src) = &export.src {-                    if let Some(required_exports) = named_export_source_imports(export) {-                        self.hoist_validated_import(-                            &src.value.to_string_lossy(),-                            export.with.as_deref(),-                            &required_exports,-                        );-                    }-                }-            }-            ModuleDecl::ExportDefaultDecl(export) => {-                self.unwrap_default_declaration(export.span, export.decl.span(), &export.decl);-            }-            ModuleDecl::ExportDefaultExpr(export) => {-                self.unwrap_default_expression(-                    export.span,-                    export.expr.span(),-                    matches!(&*export.expr, Expr::Object(..)),-                );-            }-            ModuleDecl::ExportAll(export) => {-                self.delete_span(export.span);-                if !export.type_only {-                    self.hoist_import(&export.src.value.to_string_lossy(), export.with.as_deref());-                }-            }-            ModuleDecl::TsImportEquals(import) => {-                if import.is_type_only {-                    self.delete_span(import.span);-                }-            }-            ModuleDecl::TsNamespaceExport(export) => {-                self.delete_span(export.span);-            }-            ModuleDecl::TsExportAssignment(..) => {}-        }-    }--    fn delete_span(&mut self, span: Span) {-        let replacement = if needs_statement_separator(self.code, span) {-            ";"-        } else {-            ""-        };-        self.replace_span(span, replacement.to_string());-    }--    fn replace_span(&mut self, span: Span, text: String) {-        push_span_edit(&mut self.edits, self.code, span, text);-    }--    fn hoist_import(&mut self, specifier: &str, with: Option<&ObjectLit>) {-        self.hoisted.push(format!(-            "await {};",-            await_import(self.code, specifier, with, &[])-        ));-    }--    fn hoist_validated_import(-        &mut self,-        specifier: &str,-        with: Option<&ObjectLit>,-        required_exports: &[String],-    ) {-        self.hoisted.push(format!(-            "await {};",-            await_import(self.code, specifier, with, required_exports)-        ));-    }--    fn import_decl_to_script(&self, node: &ImportDecl) -> Option<String> {-        let specifier = node.src.value.to_string_lossy();-        let with = node.with.as_deref();--        if node.specifiers.is_empty() {-            return Some(format!(-                "await {};",-                await_import(self.code, &specifier, with, &[])-            ));-        }--        let mut namespace_name = None;-        let mut default_name = None;-        let mut named = Vec::new();-        let mut required_exports = Vec::new();--        for specifier in &node.specifiers {-            match specifier {-                ImportSpecifier::Namespace(specifier) => {-                    namespace_name = Some(specifier.local.sym.to_string());-                }-                ImportSpecifier::Default(specifier) => {-                    default_name = Some(specifier.local.sym.to_string());-                    push_required_export(&mut required_exports, "default".to_string());-                }-                ImportSpecifier::Named(specifier) => {-                    if specifier.is_type_only {-                        continue;-                    }--                    let local = specifier.local.sym.to_string();-                    let imported = specifier-                        .imported-                        .as_ref()-                        .map(module_export_name)-                        .unwrap_or_else(|| local.clone());-                    push_required_export(&mut required_exports, imported.clone());-                    named.push((local, imported));-                }-            }-        }--        if namespace_name.is_none() && default_name.is_none() && named.is_empty() {-            return None;-        }--        let import = await_import(self.code, &specifier, with, &required_exports);-        if let Some(namespace_name) = namespace_name {-            let mut out = format!("const {namespace_name} = await {import};");-            if let Some(default_name) = default_name {-                out.push_str(&format!(-                    " const {default_name} = {namespace_name}.default;"-                ));-            }-            return Some(out);-        }--        // Declare the user's bindings so subsequent REPL inputs can access them.-        // Like Node's REPL transform, these capture the initial export values;-        // namespace imports still refer to the live module namespace object.-        let mut bindings = Vec::with_capacity(named.len() + usize::from(default_name.is_some()));-        if let Some(default_name) = default_name {-            bindings.push(format!("default: {default_name}"));-        }-        for (local, imported) in named {-            // Quote arbitrary export names while keeping ordinary names readable.-            let key = if is_identifier_name(&imported) {-                imported-            } else {-                json_string(&imported)-            };-            bindings.push(format!("{key}: {local}"));-        }-        Some(format!(-            "const {{ {} }} = await {import};",-            bindings.join(", ")-        ))-    }--    fn unwrap_default_declaration(-        &mut self,-        export_span: Span,-        decl_span: Span,-        decl: &DefaultDecl,-    ) {-        if matches!(decl, DefaultDecl::TsInterfaceDecl(..)) {-            self.delete_span(export_span);-            return;-        }--        let (Some((export_start, _)), Some((decl_start, decl_end))) =-            (span_range(export_span), span_range(decl_span))-        else {-            return;-        };--        push_range_edit(-            &mut self.edits,-            self.code,-            export_start,-            decl_start,-            String::new(),-        );--        if default_declaration_needs_parentheses(decl) {-            push_range_edit(-                &mut self.edits,-                self.code,-                decl_start,-                decl_start,-                "(".to_string(),-            );-            push_range_edit(-                &mut self.edits,-                self.code,-                decl_end,-                decl_end,-                ");".to_string(),-            );-        }-    }--    fn unwrap_default_expression(-        &mut self,-        export_span: Span,-        expr_span: Span,-        needs_parentheses: bool,-    ) {-        let (Some((export_start, export_end)), Some((expr_start, expr_end))) =-            (span_range(export_span), span_range(expr_span))-        else {-            return;-        };--        push_range_edit(-            &mut self.edits,-            self.code,-            export_start,-            expr_start,-            String::new(),-        );--        if needs_parentheses {-            push_range_edit(-                &mut self.edits,-                self.code,-                expr_start,-                expr_start,-                "(".to_string(),-            );-        }--        let has_separator = self-            .code-            .get(expr_end..export_end)-            .is_some_and(|tail| tail.contains(';'));-        if needs_parentheses || !has_separator {-            let mut suffix = String::new();-            if needs_parentheses {-                suffix.push(')');-            }-            if !has_separator {-                suffix.push(';');-            }-            push_range_edit(&mut self.edits, self.code, expr_end, expr_end, suffix);-        }-    }-}--fn await_import(-    code: &str,-    specifier: &str,-    with: Option<&ObjectLit>,-    required_exports: &[String],-) -> String {-    let mut out = format!("{DYNAMIC_IMPORT_NAME}({}", json_string(specifier));-    if let Some(options) = import_options_to_dynamic_options(code, with) {-        out.push_str(", ");-        out.push_str(&options);-    }-    out.push(')');-    if !required_exports.is_empty() {-        out.push_str(".then((m) => { ");-        for export_name in required_exports {-            out.push_str("if (!(");-            out.push_str(&json_string(export_name));-            out.push_str(" in m)) throw new SyntaxError(");-            out.push_str(&json_string(&format!(-                "The requested module '{specifier}' does not provide an export named \-                 '{export_name}'"-            )));-            out.push_str("); ");-        }-        out.push_str("return m; })");-    }-    out-}--fn import_options_to_dynamic_options(code: &str, with: Option<&ObjectLit>) -> Option<String> {-    let attributes = slice_span(code, with?.span)?;-    Some(format!("{{ with: {attributes} }}"))-}--fn default_declaration_needs_parentheses(decl: &DefaultDecl) -> bool {-    matches!(decl, DefaultDecl::Class(class) if class.ident.is_none())-        || matches!(decl, DefaultDecl::Fn(function) if function.ident.is_none())-}--fn is_type_only_decl(decl: &Decl) -> bool {-    matches!(decl, Decl::TsInterface(..) | Decl::TsTypeAlias(..))-}--fn named_export_source_imports(export: &NamedExport) -> Option<Vec<String>> {-    if export.type_only {-        return None;-    }--    let mut loads_source = export.specifiers.is_empty();-    let mut required_exports = Vec::new();--    for specifier in &export.specifiers {-        match specifier {-            ExportSpecifier::Named(named) => {-                if named.is_type_only {-                    continue;-                }--                loads_source = true;-                push_required_export(&mut required_exports, module_export_name(&named.orig));-            }-            ExportSpecifier::Default(..) => {-                loads_source = true;-                push_required_export(&mut required_exports, "default".to_string());-            }-            ExportSpecifier::Namespace(..) => {-                loads_source = true;-            }-        }-    }--    loads_source.then_some(required_exports)-}--fn push_required_export(required_exports: &mut Vec<String>, export_name: String) {-    if !required_exports-        .iter()-        .any(|required| required == &export_name)-    {-        required_exports.push(export_name);-    }-}--fn module_export_name(name: &ModuleExportName) -> String {-    match name {-        ModuleExportName::Ident(ident) => ident.sym.to_string(),-        ModuleExportName::Str(string) => string.value.to_string_lossy().into_owned(),-    }-}--#[derive(Clone, Debug, Eq, PartialEq)]-struct Edit {-    start: usize,-    end: usize,-    text: String,-}--fn push_span_edit(edits: &mut Vec<Edit>, code: &str, span: Span, text: String) {-    if let Some((start, end)) = span_range(span) {-        push_range_edit(edits, code, start, end, text);-    }-}--fn push_range_edit(edits: &mut Vec<Edit>, code: &str, start: usize, end: usize, text: String) {-    if start <= end && end <= code.len() {-        edits.push(Edit { start, end, text });-    }-}--fn apply_edits(mut code: String, mut edits: Vec<Edit>) -> String {-    edits.sort_by(|a, b| b.start.cmp(&a.start).then_with(|| b.end.cmp(&a.end)));--    for edit in edits {-        if edit.start <= edit.end-            && edit.end <= code.len()-            && code.is_char_boundary(edit.start)-            && code.is_char_boundary(edit.end)-        {-            code.replace_range(edit.start..edit.end, &edit.text);-        }-    }--    code-}--fn needs_statement_separator(code: &str, span: Span) -> bool {-    let Some((start, end)) = span_range(span) else {-        return false;-    };--    code[..start].chars().any(|ch| !ch.is_whitespace())-        && code[end..].chars().any(|ch| !ch.is_whitespace())-}--fn prepend_hoisted_imports(code: String, hoisted: Vec<String>) -> String {-    if hoisted.is_empty() {-        return code;-    }--    let imports = hoisted.join("\n");-    if code.starts_with("#!") {-        if let Some(line_end) = code.find('\n') {-            let insert_at = line_end + 1;-            let (hashbang, rest) = code.split_at(insert_at);-            if rest.is_empty() || rest.starts_with('\n') {-                format!("{hashbang}{imports}{rest}")-            } else {-                format!("{hashbang}{imports}\n{rest}")-            }-        } else {-            format!("{code}\n{imports}")-        }-    } else if code.is_empty() {-        imports-    } else if code.starts_with('\n') || code.starts_with("\r\n") {-        format!("{imports}{code}")-    } else {-        format!("{imports}\n{code}")-    }-}--struct ModuleFeatureCollector<'a, 'b> {-    code: &'a str,-    edits: &'b mut Vec<Edit>,-    replaced_ranges: &'b [(usize, usize)],-}--impl Visit for ModuleFeatureCollector<'_, '_> {-    fn visit_call_expr(&mut self, node: &CallExpr) {-        if matches!(node.callee, Callee::Import(..)) {-            if let Some((start, end)) = span_range(node.span) {-                if !range_is_replaced(self.replaced_ranges, start, end) {-                    let original = &self.code[start..end];-                    if original.starts_with("import") {-                        self.edits.push(Edit {-                            start,-                            end: start + "import".len(),-                            text: DYNAMIC_IMPORT_NAME.to_string(),-                        });-                    }-                }-            }-        }--        node.visit_children_with(self);-    }--    fn visit_meta_prop_expr(&mut self, node: &MetaPropExpr) {-        if node.kind == MetaPropKind::ImportMeta {-            if let Some((start, end)) = span_range(node.span) {-                if !range_is_replaced(self.replaced_ranges, start, end) {-                    self.edits.push(Edit {-                        start,-                        end,-                        text: import_meta_error_expression(),-                    });-                }-            }-        }-    }-}--fn range_is_replaced(replaced_ranges: &[(usize, usize)], start: usize, end: usize) -> bool {-    replaced_ranges-        .iter()-        .any(|(replace_start, replace_end)| start >= *replace_start && end <= *replace_end)-}--fn import_meta_error_expression() -> String {-    "(() => { throw new SyntaxError(\"Cannot use import.meta outside a module\"); })()".to_string()-}--fn is_identifier_name(value: &str) -> bool {-    let mut chars = value.chars();-    let Some(first) = chars.next() else {-        return false;-    };--    if !(first == '_' || first == '$' || first.is_ascii_alphabetic()) {-        return false;-    }--    chars.all(|ch| ch == '_' || ch == '$' || ch.is_ascii_alphanumeric())-}--enum ParseOutcome {-    Valid,-    Invalid(Vec<ParseError>),-}--fn parse_program_result(code: &str) -> ParseOutcome {-    let es = parse_program_result_with_syntax(code, Syntax::Es(es_syntax()));-    if matches!(es, ParseOutcome::Valid) {-        return es;-    }--    parse_program_result_with_syntax(code, Syntax::Typescript(ts_syntax()))-}--fn parse_program_result_with_syntax(code: &str, syntax: Syntax) -> ParseOutcome {-    let cm = Lrc::new(SourceMap::default());-    let fm = cm.new_source_file(FileName::Anon.into(), code.to_string());-    let comments = SingleThreadedComments::default();-    let lexer = Lexer::new(-        syntax,-        EsVersion::latest(),-        StringInput::from(&*fm),-        Some(&comments),-    );-    let mut parser = Parser::new_from(lexer);--    match parser.parse_program() {-        Ok(..) => {-            let errors = parser.take_errors();-            if errors.is_empty() {-                ParseOutcome::Valid-            } else {-                ParseOutcome::Invalid(errors)-            }-        }-        Err(error) => {-            let mut errors = vec![error];-            errors.extend(parser.take_errors());-            ParseOutcome::Invalid(errors)-        }-    }-}--fn parse_module(code: &str) -> Option<Module> {-    parse_module_with_syntax(code, Syntax::Es(es_syntax()))-        .or_else(|| parse_module_with_syntax(code, Syntax::Typescript(ts_syntax())))-}--fn parse_module_with_syntax(code: &str, syntax: Syntax) -> Option<Module> {-    let cm = Lrc::new(SourceMap::default());-    let fm = cm.new_source_file(FileName::Anon.into(), code.to_string());-    let comments = SingleThreadedComments::default();-    let lexer = Lexer::new(-        syntax,-        EsVersion::latest(),-        StringInput::from(&*fm),-        Some(&comments),-    );-    let mut parser = Parser::new_from(lexer);-    let module = parser.parse_module().ok()?;--    if parser.take_errors().is_empty() {-        Some(module)-    } else {-        None-    }-}--fn parses_as_program(code: &str) -> bool {-    matches!(parse_program_result(code), ParseOutcome::Valid)-}--fn lex_tokens(code: &str) -> Vec<TokenAndSpan> {-    let cm = Lrc::new(SourceMap::default());-    let fm = cm.new_source_file(FileName::Anon.into(), code.to_string());-    let comments = SingleThreadedComments::default();-    Lexer::new(-        Syntax::Es(es_syntax()),-        EsVersion::latest(),-        StringInput::from(&*fm),-        Some(&comments),-    )-    .collect()-}--fn es_syntax() -> EsSyntax {-    EsSyntax {-        auto_accessors: true,-        explicit_resource_management: true,-        import_attributes: true,-        ..Default::default()-    }-}--fn ts_syntax() -> TsSyntax {-    TsSyntax {-        decorators: true,-        ..Default::default()-    }-}--fn is_recoverable_parse_error(code: &str, error: &ParseError) -> bool {-    match error.kind() {-        SyntaxError::Eof | SyntaxError::UnterminatedTpl | SyntaxError::UnterminatedBlockComment => {-            true-        }-        SyntaxError::UnterminatedStrLit => has_trailing_line_continuation(code),-        SyntaxError::Expected(_, got) | SyntaxError::Unexpected { got, .. } => got == "<eof>",-        _ => false,-    }-}--fn has_trailing_line_continuation(code: &str) -> bool {-    code.ends_with("\\\n")-        || code.ends_with("\\\r")-        || code.ends_with("\\\r\n")-        || code.ends_with("\\\u{2028}")-        || code.ends_with("\\\u{2029}")-}--fn is_member_access_token(token: Token) -> bool {-    matches!(-        token,-        Token::Dot | Token::OptionalChain | Token::LBracket | Token::RBracket-    )-}--fn is_member_name_token(code: &str, token: &TokenAndSpan) -> bool {-    matches!(token.token, Token::Ident | Token::Str | Token::Num)-        || token.token.is_known_ident()-        || token_text_is_identifier(code, token)-}--fn is_member_identifier_token(code: &str, token: &TokenAndSpan) -> bool {-    token.token == Token::Ident-        || token.token.is_known_ident()-        || token_text_is_identifier(code, token)-}--fn token_text_is_identifier(code: &str, token: &TokenAndSpan) -> bool {-    slice_span(code, token.span).is_some_and(is_identifier_name)-}--fn slice_span(code: &str, span: Span) -> Option<&str> {-    let (start, end) = span_range(span)?;-    code.get(start..end)-}--fn span_range(span: Span) -> Option<(usize, usize)> {-    if span.lo.is_dummy() || span.hi.is_dummy() {-        return None;-    }--    let start = span.lo.0.checked_sub(1)? as usize;-    let end = span.hi.0.checked_sub(1)? as usize;-    Some((start, end))-}--fn utf16_column_to_byte_pos(code: &str, column: u32) -> usize {-    let mut utf16_pos = 0u32;--    for (byte_pos, ch) in code.char_indices() {-        if utf16_pos >= column {-            return byte_pos;-        }--        utf16_pos += ch.len_utf16() as u32;-        if utf16_pos > column {-            return byte_pos;-        }-    }--    code.len()-}--fn json_string(value: &str) -> String {-    serde_json::to_string(value).expect("serializing a string should not fail")-}--#[cfg(test)]-mod tests {-    use super::*;--    fn validated_import(specifier: &str, required_exports: &[&str]) -> String {-        validated_import_with_options(specifier, None, required_exports)-    }--    fn validated_import_with_options(-        specifier: &str,-        options: Option<&str>,-        required_exports: &[&str],-    ) -> String {-        let mut out = format!("{DYNAMIC_IMPORT_NAME}({}", json_string(specifier));-        if let Some(options) = options {-            out.push_str(", ");-            out.push_str(options);-        }-        out.push(')');--        if !required_exports.is_empty() {-            out.push_str(".then((m) => { ");-            for export_name in required_exports {-                out.push_str("if (!(");-                out.push_str(&json_string(export_name));-                out.push_str(" in m)) throw new SyntaxError(");-                out.push_str(&json_string(&format!(-                    "The requested module '{specifier}' does not provide an export named \-                     '{export_name}'"-                )));-                out.push_str("); ");-            }-            out.push_str("return m; })");-        }--        out-    }--    #[test]-    fn transform_import_forms() {-        assert_eq!(-            transform_module_syntax("import \"node:fs\";".into()).code,-            "await __nodeREPLDynamicImport(\"node:fs\");"-        );-        assert_eq!(-            transform_module_syntax("import fs from \"node:fs\";".into()).code,-            format!(-                "const {{ default: fs }} = await {};",-                validated_import("node:fs", &["default"])-            )-        );-        assert_eq!(-            transform_module_syntax("import { readFile as rf } from \"node:fs\";".into()).code,-            format!(-                "const {{ readFile: rf }} = await {};",-                validated_import("node:fs", &["readFile"])-            )-        );-        assert_eq!(-            transform_module_syntax("import { readFile as rf } from \"node:fs\";\nrf();".into())-                .code,-            format!(-                "const {{ readFile: rf }} = await {};\nrf();",-                validated_import("node:fs", &["readFile"])-            )-        );-        assert_eq!(-            transform_module_syntax("import def, * as ns from \"mod\";".into()).code,-            format!(-                "const ns = await {}; const def = ns.default;",-                validated_import("mod", &["default"])-            )-        );-        assert_eq!(-            transform_module_syntax("import def, * as ns from \"mod\";\ndef; ns;".into()).code,-            format!(-                "const ns = await {}; const def = ns.default;\ndef; ns;",-                validated_import("mod", &["default"])-            )-        );-        assert_eq!(-            transform_module_syntax("import { \"a-b\" as c } from \"mod\";".into()).code,-            format!(-                "const {{ \"a-b\": c }} = await {};",-                validated_import("mod", &["a-b"])-            )-        );-        assert_eq!(-            transform_module_syntax("import { \"a-b\" as c } from \"mod\";\nc;".into()).code,-            format!(-                "const {{ \"a-b\": c }} = await {};\nc;",-                validated_import("mod", &["a-b"])-            )-        );-        assert_eq!(-            transform_module_syntax(-                "import data from \"./data.json\" with { type: \"json\" };".into()-            )-            .code,-            format!(-                "const {{ default: data }} = await {};",-                validated_import_with_options(-                    "./data.json",-                    Some("{ with: { type: \"json\" } }"),-                    &["default"]-                )-            )-        );-        assert_eq!(-            transform_module_syntax("console.log(typeof fs);\nimport fs from \"node:fs\";".into())-                .code,-            format!(-                "const {{ default: fs }} = await {};\nconsole.log(typeof fs);\n",-                validated_import("node:fs", &["default"])-            )-        );-    }--    #[test]-    fn transform_export_forms() {-        assert_eq!(-            transform_module_syntax("export const x = 1;".into()).code,-            "const x = 1;"-        );-        assert_eq!(-            transform_module_syntax("export default value;".into()).code,-            "value;"-        );-        assert_eq!(-            transform_module_syntax("export default foo; doSomething();".into()).code,-            "foo; doSomething();"-        );-        assert_eq!(-            transform_module_syntax("export default { ...config };".into()).code,-            "({ ...config });"-        );-        assert_eq!(-            transform_module_syntax("export default function () {}".into()).code,-            "(function () {});"-        );-        assert_eq!(-            transform_module_syntax(-                "export default function f() { return import(\"node:fs\"); }".into()-            )-            .code,-            "function f() { return __nodeREPLDynamicImport(\"node:fs\"); }"-        );-        assert_eq!(-            transform_module_syntax(-                "export default function () { return import(\"node:fs\"); }".into()-            )-            .code,-            "(function () { return __nodeREPLDynamicImport(\"node:fs\"); });"-        );-        assert_eq!(-            transform_module_syntax("export default class {}".into()).code,-            "(class {});"-        );-        assert_eq!(transform_module_syntax("export { x };".into()).code, "");-        assert_eq!(-            transform_module_syntax("export * from \"mod\";".into()).code,-            "await __nodeREPLDynamicImport(\"mod\");"-        );-        assert_eq!(-            transform_module_syntax("console.log(1);\nexport { value } from \"mod\";".into()).code,-            format!(-                "await {};\nconsole.log(1);\n",-                validated_import("mod", &["value"])-            )-        );-    }--    #[test]-    fn transform_preserves_missing_export_failures() {-        let missing_default = transform_module_syntax("import missing from \"mod\";".into());-        assert!(missing_default.code.contains("\"default\" in m"));-        assert!(missing_default.code.contains("export named 'default'"));--        let missing_named = transform_module_syntax("import { missing, ok } from \"mod\";".into());-        assert!(missing_named.code.contains("\"missing\" in m"));-        assert!(missing_named.code.contains("\"ok\" in m"));--        let re_export = transform_module_syntax("export { missing } from \"mod\";".into());-        assert!(re_export.code.contains("\"missing\" in m"));-    }--    #[test]-    fn transform_preserves_import_binding_semantics() {-        assert_eq!(-            transform_module_syntax("import { spec } from \"m\";\nimport(spec);".into()).code,-            format!(-                "const {{ spec: spec }} = await {};\n__nodeREPLDynamicImport(spec);",-                validated_import("m", &["spec"])-            )-        );--        assert_eq!(-            transform_module_syntax(-                "import { x } from \"m\";\nfunction f(x) { return x; }\nx;".into()-            )-            .code,-            format!(-                "const {{ x: x }} = await {};\nfunction f(x) {{ return x; }}\nx;",-                validated_import("m", &["x"])-            )-        );--        assert_eq!(-            transform_module_syntax("import { x } from \"m\";\n{ const x = 1; x; }\nx;".into())-                .code,-            format!(-                "const {{ x: x }} = await {};\n{{ const x = 1; x; }}\nx;",-                validated_import("m", &["x"])-            )-        );--        assert_eq!(-            transform_module_syntax("import { x } from \"m\";\ntry {} catch (x) { x; }\nx;".into())-                .code,-            format!(-                "const {{ x: x }} = await {};\ntry {{}} catch (x) {{ x; }}\nx;",-                validated_import("m", &["x"])-            )-        );--        assert_eq!(-            transform_module_syntax(-                "import { x } from \"m\";\nfunction f(){ if (ok) { var x = 1; } return x; }\nx;"-                    .into()-            )-            .code,-            format!(-                "const {{ x: x }} = await {};\nfunction f(){{ if (ok) {{ var x = 1; }} return x; \-                 }}\nx;",-                validated_import("m", &["x"])-            )-        );--        assert_eq!(-            transform_module_syntax("import { readFile } from \"node:fs\";\nreadFile = 1;".into())-                .code,-            format!(-                "const {{ readFile: readFile }} = await {};\nreadFile = 1;",-                validated_import("node:fs", &["readFile"])-            )-        );--        assert_eq!(-            transform_module_syntax(-                "import { readFile } from \"node:fs\";\n({ readFile } = obj);".into()-            )-            .code,-            format!(-                "const {{ readFile: readFile }} = await {};\n({{ readFile }} = obj);",-                validated_import("node:fs", &["readFile"])-            )-        );--        assert_eq!(-            transform_module_syntax("import { readFile } from \"node:fs\";\nreadFile++;".into())-                .code,-            format!(-                "const {{ readFile: readFile }} = await {};\nreadFile++;",-                validated_import("node:fs", &["readFile"])-            )-        );-    }--    #[test]-    fn transform_dynamic_import_and_noops() {-        assert_eq!(-            transform_module_syntax("const mod = import(\"node:fs\");".into()).code,-            "const mod = __nodeREPLDynamicImport(\"node:fs\");"-        );--        let no_module_syntax = transform_module_syntax("const importName = \"import\";".into());-        assert!(!no_module_syntax.had_module_syntax);-        assert_eq!(no_module_syntax.code, "const importName = \"import\";");--        let incomplete = transform_module_syntax("import {".into());-        assert!(!incomplete.had_module_syntax);-        assert_eq!(incomplete.code, "import {");--        let import_meta = transform_module_syntax("console.log(import.meta.url);".into());-        assert!(import_meta.had_module_syntax);-        assert!(import_meta-            .code-            .contains("Cannot use import.meta outside a module"));-    }--    #[test]-    fn transform_preserves_statement_boundaries() {-        let hashbang = transform_module_syntax(-            "#!/usr/bin/env node\nimport fs from \"node:fs\";\nfs.readFile;".into(),-        )-        .code;-        assert!(hashbang.starts_with("#!/usr/bin/env node\nconst { default: fs } = await "));-        assert!(hashbang.contains("\nfs.readFile;"));--        assert_eq!(-            transform_module_syntax("foo()\nimport \"x\";\n[1].forEach(bar)".into()).code,-            "await __nodeREPLDynamicImport(\"x\");\nfoo()\n;\n[1].forEach(bar)"-        );-    }--    #[test]-    fn transform_typescript_module_syntax() {-        assert_eq!(-            transform_module_syntax("import fs from \"node:fs\";\nconst x: number = 1;".into())-                .code,-            format!(-                "const {{ default: fs }} = await {};\nconst x: number = 1;",-                validated_import("node:fs", &["default"])-            )-        );--        let type_only = transform_module_syntax(-            "import type { Foo } from \"types\";\nexport type { Foo };\nconst x: number = 1;"-                .into(),-        );-        assert!(type_only.had_module_syntax);-        assert!(!type_only.code.contains("__nodeREPLDynamicImport"));-        assert!(!type_only.code.contains("import type"));-        assert!(!type_only.code.contains("export type"));--        let re_export_type =-            transform_module_syntax("export { type Foo } from \"types\";\nconst x = 1;".into());-        assert!(!re_export_type.code.contains("__nodeREPLDynamicImport"));-        assert_eq!(re_export_type.code, "\nconst x = 1;");--        let import_equals =-            transform_module_syntax("import type Foo = require(\"foo\");\nconst x = 1;".into());-        assert!(import_equals.had_module_syntax);-        assert_eq!(import_equals.code, "\nconst x = 1;");--        let namespace_export =-            transform_module_syntax("export as namespace Foo;\nconst x = 1;".into());-        assert!(namespace_export.had_module_syntax);-        assert_eq!(namespace_export.code, "\nconst x = 1;");-    }--    #[test]-    fn first_expression_from_error_column() {-        assert_eq!(-            get_first_expression("assert(value)".into(), 6),-            "assert(value)"-        );-        assert_eq!(get_first_expression("ok(value)".into(), 3), "ok(value)");-        assert_eq!(-            get_first_expression("assert.ok(value)".into(), 9),-            "assert.ok(value)"-        );-        assert_eq!(-            get_first_expression("assert['ok'](value)".into(), 12),-            "assert['ok'](value)"-        );-        assert_eq!(-            get_first_expression("assert[method + suffix](value)".into(), 23),-            "assert[method + suffix](value)"-        );-        assert_eq!(-            get_first_expression("assert.deepEqual(foo(), 1)".into(), 17),-            "assert.deepEqual(foo(), 1)"-        );-        assert_eq!(-            get_first_expression("assert?.ok(value)".into(), 10),-            "assert?.ok(value)"-        );-        assert_eq!(-            get_first_expression("a(); assert.ok(value); b()".into(), 13),-            "assert.ok(value)"-        );-        assert_eq!(-            get_first_expression("const 🍣 = 1; assert.ok(🍣)".into(), 23),-            "assert.ok(🍣)"-        );-    }--    #[test]-    fn valid_syntax_checks() {-        assert!(is_valid_syntax("await foo".into()));-        assert!(is_valid_syntax("{ value: 1 }".into()));-        assert!(is_valid_syntax("foo + bar".into()));-        assert!(is_valid_syntax("const x: number = 1".into()));-        assert!(!is_valid_syntax("function foo(".into()));-    }--    #[test]-    fn recoverable_syntax_checks() {-        assert!(is_recoverable_error("function foo() {".into()));-        assert!(is_recoverable_error("{".into()));-        assert!(is_recoverable_error("`template".into()));-        assert!(is_recoverable_error("/* comment".into()));-        assert!(is_recoverable_error("\"continued\\\n".into()));-        assert!(!is_recoverable_error("const x: number = 1".into()));-        assert!(!is_recoverable_error("{ value: 1 }".into()));-        assert!(!is_recoverable_error("{}".into()));-        assert!(!is_recoverable_error("2e".into()));-        assert!(!is_recoverable_error("\"unterminated".into()));-    }-}