261020.1 · 1d7e0126f3e601103d93586a6f872144f2789dad
chore: self-maintain Node.js features
reading declarations
Changes to deps/swc/bindings/binding_nodejs_support_wasm/. All files
deps/swc/bindings/binding_nodejs_support_wasm/.cargo/config.toml
@@ -1,3 +0,0 @@-[target.wasm32-unknown-unknown]-# support more stack size due to issue: https://github.com/swc-project/swc/issues/10207-rustflags = ["-C", "link-args=-z stack-size=2097152"]
deps/swc/bindings/binding_nodejs_support_wasm/Cargo.toml
@@ -1,45 +0,0 @@-[package]-authors = ["강동윤 <kdy1997.dev@gmail.com>"]-description = "Node.js support helpers compiled to WebAssembly"-edition = { workspace = true }-license = { workspace = true }-name = "binding_nodejs_support_wasm"-publish = false-repository = { workspace = true }-version = "1.16.18"--[lib]-bench = false-crate-type = ["cdylib"]--[features]-nightly = ["swc_ts_fast_strip/nightly"]--[dependencies]-anyhow = { workspace = true }-js-sys = { workspace = true }-miette = { workspace = true }-owo-colors = { workspace = true }-serde = { workspace = true, features = ["derive"] }-serde-wasm-bindgen = { workspace = true }-serde_json = { workspace = true }-swc_common = { path = "../../crates/swc_common" }-swc_ecma_ast = { path = "../../crates/swc_ecma_ast" }-swc_ecma_parser = { path = "../../crates/swc_ecma_parser", features = [- "unstable",-] }-swc_ecma_visit = { path = "../../crates/swc_ecma_visit" }-swc_error_reporters = { path = "../../crates/swc_error_reporters" }-swc_ts_fast_strip = { path = "../../crates/swc_ts_fast_strip", features = [- "wasm-bindgen",-] }-tracing = { workspace = true, features = ["max_level_off"] }-unicode-width = { workspace = true }-wasm-bindgen = { workspace = true, features = ["enable-interning"] }-wasm-bindgen-futures = { workspace = true }--[dev-dependencies]-testing = { path = "../../crates/testing" }--[package.metadata.wasm-pack.profile.release]-wasm-opt = false
deps/swc/bindings/binding_nodejs_support_wasm/LICENSE
@@ -1,201 +0,0 @@- Apache License- Version 2.0, January 2004- http://www.apache.org/licenses/--TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION--1. Definitions.-- "License" shall mean the terms and conditions for use, reproduction,- and distribution as defined by Sections 1 through 9 of this document.-- "Licensor" shall mean the copyright owner or entity authorized by- the copyright owner that is granting the License.-- "Legal Entity" shall mean the union of the acting entity and all- other entities that control, are controlled by, or are under common- control with that entity. For the purposes of this definition,- "control" means (i) the power, direct or indirect, to cause the- direction or management of such entity, whether by contract or- otherwise, or (ii) ownership of fifty percent (50%) or more of the- outstanding shares, or (iii) beneficial ownership of such entity.-- "You" (or "Your") shall mean an individual or Legal Entity- exercising permissions granted by this License.-- "Source" form shall mean the preferred form for making modifications,- including but not limited to software source code, documentation- source, and configuration files.-- "Object" form shall mean any form resulting from mechanical- transformation or translation of a Source form, including but- not limited to compiled object code, generated documentation,- and conversions to other media types.-- "Work" shall mean the work of authorship, whether in Source or- Object form, made available under the License, as indicated by a- copyright notice that is included in or attached to the work- (an example is provided in the Appendix below).-- "Derivative Works" shall mean any work, whether in Source or Object- form, that is based on (or derived from) the Work and for which the- editorial revisions, annotations, elaborations, or other modifications- represent, as a whole, an original work of authorship. For the purposes- of this License, Derivative Works shall not include works that remain- separable from, or merely link (or bind by name) to the interfaces of,- the Work and Derivative Works thereof.-- "Contribution" shall mean any work of authorship, including- the original version of the Work and any modifications or additions- to that Work or Derivative Works thereof, that is intentionally- submitted to Licensor for inclusion in the Work by the copyright owner- or by an individual or Legal Entity authorized to submit on behalf of- the copyright owner. For the purposes of this definition, "submitted"- means any form of electronic, verbal, or written communication sent- to the Licensor or its representatives, including but not limited to- communication on electronic mailing lists, source code control systems,- and issue tracking systems that are managed by, or on behalf of, the- Licensor for the purpose of discussing and improving the Work, but- excluding communication that is conspicuously marked or otherwise- designated in writing by the copyright owner as "Not a Contribution."-- "Contributor" shall mean Licensor and any individual or Legal Entity- on behalf of whom a Contribution has been received by Licensor and- subsequently incorporated within the Work.--2. Grant of Copyright License. Subject to the terms and conditions of- this License, each Contributor hereby grants to You a perpetual,- worldwide, non-exclusive, no-charge, royalty-free, irrevocable- copyright license to reproduce, prepare Derivative Works of,- publicly display, publicly perform, sublicense, and distribute the- Work and such Derivative Works in Source or Object form.--3. Grant of Patent License. Subject to the terms and conditions of- this License, each Contributor hereby grants to You a perpetual,- worldwide, non-exclusive, no-charge, royalty-free, irrevocable- (except as stated in this section) patent license to make, have made,- use, offer to sell, sell, import, and otherwise transfer the Work,- where such license applies only to those patent claims licensable- by such Contributor that are necessarily infringed by their- Contribution(s) alone or by combination of their Contribution(s)- with the Work to which such Contribution(s) was submitted. If You- institute patent litigation against any entity (including a- cross-claim or counterclaim in a lawsuit) alleging that the Work- or a Contribution incorporated within the Work constitutes direct- or contributory patent infringement, then any patent licenses- granted to You under this License for that Work shall terminate- as of the date such litigation is filed.--4. Redistribution. You may reproduce and distribute copies of the- Work or Derivative Works thereof in any medium, with or without- modifications, and in Source or Object form, provided that You- meet the following conditions:-- (a) You must give any other recipients of the Work or- Derivative Works a copy of this License; and-- (b) You must cause any modified files to carry prominent notices- stating that You changed the files; and-- (c) You must retain, in the Source form of any Derivative Works- that You distribute, all copyright, patent, trademark, and- attribution notices from the Source form of the Work,- excluding those notices that do not pertain to any part of- the Derivative Works; and-- (d) If the Work includes a "NOTICE" text file as part of its- distribution, then any Derivative Works that You distribute must- include a readable copy of the attribution notices contained- within such NOTICE file, excluding those notices that do not- pertain to any part of the Derivative Works, in at least one- of the following places: within a NOTICE text file distributed- as part of the Derivative Works; within the Source form or- documentation, if provided along with the Derivative Works; or,- within a display generated by the Derivative Works, if and- wherever such third-party notices normally appear. The contents- of the NOTICE file are for informational purposes only and- do not modify the License. You may add Your own attribution- notices within Derivative Works that You distribute, alongside- or as an addendum to the NOTICE text from the Work, provided- that such additional attribution notices cannot be construed- as modifying the License.-- You may add Your own copyright statement to Your modifications and- may provide additional or different license terms and conditions- for use, reproduction, or distribution of Your modifications, or- for any such Derivative Works as a whole, provided Your use,- reproduction, and distribution of the Work otherwise complies with- the conditions stated in this License.--5. Submission of Contributions. Unless You explicitly state otherwise,- any Contribution intentionally submitted for inclusion in the Work- by You to the Licensor shall be under the terms and conditions of- this License, without any additional terms or conditions.- Notwithstanding the above, nothing herein shall supersede or modify- the terms of any separate license agreement you may have executed- with Licensor regarding such Contributions.--6. Trademarks. This License does not grant permission to use the trade- names, trademarks, service marks, or product names of the Licensor,- except as required for reasonable and customary use in describing the- origin of the Work and reproducing the content of the NOTICE file.--7. Disclaimer of Warranty. Unless required by applicable law or- agreed to in writing, Licensor provides the Work (and each- Contributor provides its Contributions) on an "AS IS" BASIS,- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or- implied, including, without limitation, any warranties or conditions- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A- PARTICULAR PURPOSE. You are solely responsible for determining the- appropriateness of using or redistributing the Work and assume any- risks associated with Your exercise of permissions under this License.--8. Limitation of Liability. In no event and under no legal theory,- whether in tort (including negligence), contract, or otherwise,- unless required by applicable law (such as deliberate and grossly- negligent acts) or agreed to in writing, shall any Contributor be- liable to You for damages, including any direct, indirect, special,- incidental, or consequential damages of any character arising as a- result of this License or out of the use or inability to use the- Work (including but not limited to damages for loss of goodwill,- work stoppage, computer failure or malfunction, or any and all- other commercial damages or losses), even if such Contributor- has been advised of the possibility of such damages.--9. Accepting Warranty or Additional Liability. While redistributing- the Work or Derivative Works thereof, You may choose to offer,- and charge a fee for, acceptance of support, warranty, indemnity,- or other liability obligations and/or rights consistent with this- License. However, in accepting such obligations, You may act only- on Your own behalf and on Your sole responsibility, not on behalf- of any other Contributor, and only if You agree to indemnify,- defend, and hold each Contributor harmless for any liability- incurred by, or claims asserted against, such Contributor by reason- of your accepting any such warranty or additional liability.--END OF TERMS AND CONDITIONS--APPENDIX: How to apply the Apache License to your work.-- To apply the Apache License to your work, attach the following- boilerplate notice, with the fields enclosed by brackets "[]"- replaced with your own identifying information. (Don't include- the brackets!) The text should be enclosed in the appropriate- comment syntax for the file format. We also recommend that a- file or class name and description of purpose be included on the- same "printed page" as the copyright notice for easier- identification within third-party archives.--Copyright 2024 SWC contributors.--Licensed under the Apache License, Version 2.0 (the "License");-you may not use this file except in compliance with the License.-You may obtain a copy of the License at-- http://www.apache.org/licenses/LICENSE-2.0--Unless required by applicable law or agreed to in writing, software-distributed under the License is distributed on an "AS IS" BASIS,-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.-See the License for the specific language governing permissions and-limitations under the License.
deps/swc/bindings/binding_nodejs_support_wasm/README.md
@@ -1,68 +0,0 @@-# @swc/nodejs-support-wasm--This package provides a standalone WebAssembly binding for Node.js-integrations. Its Rust crate, source, build, and test setup are independent from-`@swc/wasm-typescript`.--It includes the TypeScript transform API from `@swc/wasm-typescript` and-additional helpers for module syntax transformation, assertion locations, and-syntax validation.--## API--- `transform` and `transformSync`-- `transformModuleSyntax`-- `getFirstExpression`-- `isValidSyntax`-- `isRecoverableError`-- `tokenize`-- `findTopLevelAwaits`--All source arguments accept a string or a UTF-8 `Uint8Array`.--### Syntax highlighting--`tokenize(src)` returns `{ kind, start, end }[]` in source order. The token kinds-are `keyword`, `identifier`, `number`, `bigint`, `string`, `template`, `regexp`,-`comment`, and `punctuator`. Offsets are zero-based UTF-16 code units with an-exclusive end, so `src.slice(start, end)` extracts the token. Comments include-their delimiters; hashbangs are comments. Whitespace and EOF are omitted.-Template text includes its backticks, while `${` and `}` are punctuators and-interpolated expressions have their own tokens.--This helper parses JavaScript to distinguish regular expressions from division-and track template substitutions. On incomplete or invalid input it returns the-tokens captured before parsing stopped, without throwing a syntax error. It is-intended for REPL highlighting, not syntax validation or TypeScript tokenization.--### Top-level await locations--`findTopLevelAwaits(src)` returns `{ line, column }[]` in source order for `await`-expressions, `for await` loops, and `await using` declarations in module scope.-Lines start at 1 and UTF-16 columns start at 0, matching Acorn. Each location is-the start of the construct (`for` for a `for await` loop). Function bodies,-including methods and arrow functions, are excluded; class heritage expressions,-computed keys, and method decorators are included. JavaScript and TypeScript are-supported, and invalid or incomplete modules return an empty array.--### REPL imports--`transformModuleSyntax(src)` hoists imports and declares their local bindings,-so a REPL can retain them for subsequent inputs. For example, a default import-declares `const { default: fs } = await __nodeREPLDynamicImport(...)` after-validating that the export exists. The caller supplies `__nodeREPLDynamicImport`-and evaluates the output with top-level await support.--Default and named bindings capture the initial export values, matching Node's-REPL transform; namespace bindings retain the module namespace object. References-and assignments are left intact so normal JavaScript scope, call, and `const`-semantics apply before any further rewriting performed by the host REPL.--## Contributing--See [the main repository](https://github.com/swc-project/swc)'s contributing-guide.--## License--Apache 2.0
deps/swc/bindings/binding_nodejs_support_wasm/package.json
@@ -1,5 +0,0 @@-{- "devDependencies": {- "@rstest/core": "^0.7.8"- }-}
deps/swc/bindings/binding_nodejs_support_wasm/rstest.config.mjs
@@ -1,4 +0,0 @@-export default {- include: ["__tests__/**/*.{js,jsx,ts,tsx}"],- globals: true,-};
deps/swc/bindings/binding_nodejs_support_wasm/scripts/build.sh
@@ -1,16 +0,0 @@-#!/usr/bin/env bash-set -eux--export CARGO_PROFILE_RELEASE_LTO="fat"-export CARGO_PROFILE_RELEASE_OPT_LEVEL="z"--wasm-pack build \- --out-name wasm \- --release \- --scope=swc \- --target nodejs \- "$@"-ls -al ./pkg--node ./scripts/patch.mjs-cp ./README.md ./pkg/README.md
deps/swc/bindings/binding_nodejs_support_wasm/scripts/patch.mjs
@@ -1,31 +0,0 @@-import fs from "node:fs/promises";--const rawWasmPath = "pkg/wasm_bg.wasm";-const wasmJsPath = "pkg/wasm.js";-const packageJsonPath = "pkg/package.json";-const rawWasmFile = await fs.readFile(rawWasmPath);-const origJsFile = await fs.readFile(wasmJsPath, "utf8");--const base64 = rawWasmFile.toString("base64");-const patchedJsFile = origJsFile- .replace(- `const path = require('path').join(__dirname, 'wasm_bg.wasm');`,- ""- )- .replace(", fatal: true", "")- .replace(- `const bytes = require('fs').readFileSync(path);`,- `-const { Buffer } = require('node:buffer');-const bytes = Buffer.from('${base64}', 'base64');`- );--await fs.writeFile(wasmJsPath, patchedJsFile);-await fs.unlink(rawWasmPath);--const pkgJsonFile = await fs.readFile(packageJsonPath, "utf8");-const pkgJson = JSON.parse(pkgJsonFile);-pkgJson.name = "@swc/nodejs-support-wasm";-pkgJson.description = "SWC WebAssembly helpers for Node.js";-pkgJson.files = pkgJson.files.filter((file) => file !== "wasm_bg.wasm");-await fs.writeFile(packageJsonPath, JSON.stringify(pkgJson, null, 2));
deps/swc/bindings/binding_nodejs_support_wasm/scripts/test.sh
@@ -1,9 +0,0 @@-#!/usr/bin/env bash--set -eu--./scripts/build.sh-npx rstest "$@"--./scripts/build.sh --features nightly-npx rstest "$@"
deps/swc/bindings/binding_nodejs_support_wasm/src/error_reporter.rs
@@ -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
@@ -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
@@ -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()));- }-}