mirror of
https://github.com/swc-project/swc.git
synced 2025-01-03 19:14:01 +03:00
refactor(es/minifier): Remove unused crates (#3395)
This commit is contained in:
parent
839d0ac480
commit
74b433080b
1
.github/workflows/cargo.yml
vendored
1
.github/workflows/cargo.yml
vendored
@ -107,7 +107,6 @@ jobs:
|
||||
- swc_ecma_codegen
|
||||
- swc_ecma_codegen_macros
|
||||
- swc_ecma_dep_graph
|
||||
- swc_ecma_diff
|
||||
- swc_ecma_ext_transforms
|
||||
- swc_ecma_lints
|
||||
- swc_ecma_loader
|
||||
|
73
Cargo.lock
generated
73
Cargo.lock
generated
@ -620,35 +620,6 @@ dependencies = [
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dbg-swc"
|
||||
version = "0.10.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"pretty_assertions 1.0.0",
|
||||
"rayon",
|
||||
"structopt",
|
||||
"swc_atoms",
|
||||
"swc_common",
|
||||
"swc_ecma_ast",
|
||||
"swc_ecma_codegen",
|
||||
"swc_ecma_dep_graph",
|
||||
"swc_ecma_diff",
|
||||
"swc_ecma_loader",
|
||||
"swc_ecma_minifier",
|
||||
"swc_ecma_parser",
|
||||
"swc_ecma_transforms_base",
|
||||
"swc_ecma_transforms_react",
|
||||
"swc_ecma_transforms_typescript",
|
||||
"swc_ecma_utils",
|
||||
"swc_ecma_visit",
|
||||
"swc_node_comments",
|
||||
"swc_timer",
|
||||
"tempfile",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "debug_unreachable"
|
||||
version = "0.1.1"
|
||||
@ -1849,18 +1820,6 @@ dependencies = [
|
||||
"output_vt100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0cfe1b2403f172ba0f234e500906ee0a3e493fb81092dac23ebefe129301cc"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"ctor",
|
||||
"diff",
|
||||
"output_vt100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "0.1.5"
|
||||
@ -2937,7 +2896,7 @@ dependencies = [
|
||||
name = "swc_ecma_dep_graph"
|
||||
version = "0.58.0"
|
||||
dependencies = [
|
||||
"pretty_assertions 0.7.2",
|
||||
"pretty_assertions",
|
||||
"swc_atoms",
|
||||
"swc_common",
|
||||
"swc_ecma_ast",
|
||||
@ -2946,22 +2905,6 @@ dependencies = [
|
||||
"testing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_diff"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"auto_impl",
|
||||
"num-bigint",
|
||||
"string_cache",
|
||||
"swc_atoms",
|
||||
"swc_common",
|
||||
"swc_ecma_ast",
|
||||
"swc_ecma_codegen",
|
||||
"swc_ecma_parser",
|
||||
"testing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "swc_ecma_ext_transforms"
|
||||
version = "0.50.0"
|
||||
@ -3021,7 +2964,7 @@ dependencies = [
|
||||
"backtrace",
|
||||
"indexmap",
|
||||
"once_cell",
|
||||
"pretty_assertions 0.7.2",
|
||||
"pretty_assertions",
|
||||
"rayon",
|
||||
"regex",
|
||||
"retain_mut",
|
||||
@ -3052,7 +2995,7 @@ dependencies = [
|
||||
"enum_kind",
|
||||
"lexical",
|
||||
"num-bigint",
|
||||
"pretty_assertions 0.7.2",
|
||||
"pretty_assertions",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
@ -3078,7 +3021,7 @@ dependencies = [
|
||||
"dashmap",
|
||||
"indexmap",
|
||||
"once_cell",
|
||||
"pretty_assertions 0.7.2",
|
||||
"pretty_assertions",
|
||||
"semver 1.0.4",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -3100,7 +3043,7 @@ dependencies = [
|
||||
name = "swc_ecma_transforms"
|
||||
version = "0.114.3"
|
||||
dependencies = [
|
||||
"pretty_assertions 0.7.2",
|
||||
"pretty_assertions",
|
||||
"sourcemap",
|
||||
"swc_atoms",
|
||||
"swc_common",
|
||||
@ -3408,7 +3351,7 @@ dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
"copyless",
|
||||
"pretty_assertions 0.7.2",
|
||||
"pretty_assertions",
|
||||
"rayon",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -3473,7 +3416,7 @@ dependencies = [
|
||||
"dashmap",
|
||||
"is-macro",
|
||||
"once_cell",
|
||||
"pretty_assertions 0.7.2",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -3661,7 +3604,7 @@ dependencies = [
|
||||
"ansi_term",
|
||||
"difference",
|
||||
"once_cell",
|
||||
"pretty_assertions 0.7.2",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"swc_common",
|
||||
|
@ -1,12 +1,10 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"crates/dbg-swc",
|
||||
"crates/jsdoc",
|
||||
"crates/node",
|
||||
"crates/swc_cli",
|
||||
"crates/swc_css",
|
||||
"crates/swc_ecmascript",
|
||||
"crates/swc_ecma_diff",
|
||||
"crates/swc_ecma_lints",
|
||||
"crates/swc_estree_compat",
|
||||
"crates/swc_plugin",
|
||||
|
@ -1,37 +0,0 @@
|
||||
[package]
|
||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||
description = "Debug tools for swc"
|
||||
documentation = "https://rustdoc.swc.rs/dbg-swc/"
|
||||
edition = "2021"
|
||||
include = ["Cargo.toml", "src/**/*.rs"]
|
||||
license = "Apache-2.0"
|
||||
name = "dbg-swc"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.10.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
pretty_assertions = "1.0.0"
|
||||
rayon = "1"
|
||||
structopt = "0.3.25"
|
||||
swc_atoms = {version = "0.2.9", path = "../swc_atoms/"}
|
||||
swc_common = {version = "0.17.0", path = "../swc_common/", features = ["concurrent"]}
|
||||
swc_ecma_ast = {version = "0.65.0", path = "../swc_ecma_ast/"}
|
||||
swc_ecma_codegen = {version = "0.89.0", path = "../swc_ecma_codegen"}
|
||||
swc_ecma_dep_graph = {version = "0.58.0", path = "../swc_ecma_dep_graph/"}
|
||||
swc_ecma_diff = {version = "0.6.0", path = "../swc_ecma_diff/"}
|
||||
swc_ecma_loader = {version = "0.28.0", path = "../swc_ecma_loader/", features = ["lru", "node", "tsc"]}
|
||||
swc_ecma_minifier = {version = "0.71.0", path = "../swc_ecma_minifier/"}
|
||||
swc_ecma_parser = {version = "0.87.0", path = "../swc_ecma_parser/"}
|
||||
swc_ecma_transforms_base = {version = "0.57.0", path = "../swc_ecma_transforms_base/"}
|
||||
swc_ecma_transforms_react = {version = "0.77.0", path = "../swc_ecma_transforms_react/"}
|
||||
swc_ecma_transforms_typescript = {version = "0.79.0", path = "../swc_ecma_transforms_typescript/"}
|
||||
swc_ecma_utils = {version = "0.64.0", path = "../swc_ecma_utils/"}
|
||||
swc_ecma_visit = {version = "0.51.0", path = "../swc_ecma_visit/"}
|
||||
swc_node_comments = {version = "0.4.0", path = "../swc_node_comments/"}
|
||||
swc_timer = {version = "0.4.0", path = "../swc_timer/"}
|
||||
tempfile = "3.2.0"
|
||||
tracing = "0.1.29"
|
||||
tracing-subscriber = {version = "0.3.5", features = ["env-filter"]}
|
@ -1,13 +0,0 @@
|
||||
# dbg-swc
|
||||
|
||||
## `dbg-swc reduce-min`
|
||||
|
||||
```
|
||||
dbg-swc reduce-min --build '' --test 'npm test.spec' --working-dir ~/src/ionic-framework/packages/react/ ~/src/ionic-framework/packages/react/src/index.ts
|
||||
```
|
||||
|
||||
(TODO) e.g. next.js app
|
||||
|
||||
```sh
|
||||
dbg-swc reduce-min --build 'rm -rf .next && npx next build' --test 'dbg-swc grab-console http://localhost:3000 --script test.js --start "npm start"' ~/your/next/app/pages/index.js ~/your/next/app/pages/index.js
|
||||
```
|
@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
cargo install --locked --debug --path .
|
@ -1,5 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
|
||||
cargo run -- $@
|
@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
./scripts/install.sh
|
||||
|
||||
dbg-swc reduce-min --build 'rm -rf .next && npx next build' --test 'dbg-swc grab-console http://localhost:3000 --script test.js --start "npm start"' $@
|
@ -1,71 +0,0 @@
|
||||
use self::{minified::DiffMinifiedCommand, reduce_min::ReduceMinCommand};
|
||||
use anyhow::Result;
|
||||
use std::{env, io::stderr, str::FromStr, sync::Arc, time::Instant};
|
||||
use structopt::StructOpt;
|
||||
use swc_common::{
|
||||
errors::{Handler, HANDLER},
|
||||
SourceMap, GLOBALS,
|
||||
};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
mod minified;
|
||||
mod reduce_min;
|
||||
mod util;
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
enum Cmd {
|
||||
DiffMin(DiffMinifiedCommand),
|
||||
ReduceMin(ReduceMinCommand),
|
||||
}
|
||||
|
||||
struct Track {
|
||||
start: Instant,
|
||||
}
|
||||
|
||||
impl Drop for Track {
|
||||
fn drop(&mut self) {
|
||||
eprintln!("Done in {:?}", self.start.elapsed());
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let globals = swc_common::Globals::default();
|
||||
let cm = Arc::new(SourceMap::default());
|
||||
let handler = Handler::with_emitter_writer(Box::new(stderr()), Some(cm.clone()));
|
||||
|
||||
let _logger = {
|
||||
let log_env = env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string());
|
||||
|
||||
let logger = tracing_subscriber::FmtSubscriber::builder()
|
||||
.without_time()
|
||||
.with_target(false)
|
||||
.with_ansi(true)
|
||||
.with_env_filter(EnvFilter::from_str(&log_env).unwrap())
|
||||
.with_test_writer()
|
||||
.pretty()
|
||||
.finish();
|
||||
|
||||
tracing::subscriber::set_global_default(logger)
|
||||
};
|
||||
|
||||
let _track = Track {
|
||||
start: Instant::now(),
|
||||
};
|
||||
|
||||
let cmd = Cmd::from_args();
|
||||
|
||||
GLOBALS.set(&globals, || {
|
||||
HANDLER.set(&handler, || {
|
||||
match cmd {
|
||||
Cmd::DiffMin(cmd) => {
|
||||
cmd.run(cm.clone())?;
|
||||
}
|
||||
Cmd::ReduceMin(cmd) => {
|
||||
cmd.run(cm.clone())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
})
|
||||
}
|
@ -1,192 +0,0 @@
|
||||
use crate::util::{parse, print_js};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use std::{
|
||||
io::Write,
|
||||
path::PathBuf,
|
||||
process::{Command, Stdio},
|
||||
sync::Arc,
|
||||
};
|
||||
use structopt::StructOpt;
|
||||
use swc_common::{util::take::Take, FileName, Mark, SourceMap};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_diff::Diff;
|
||||
use swc_ecma_minifier::option::{ExtraOptions, MinifyOptions};
|
||||
use swc_ecma_transforms_base::{fixer::fixer, resolver::resolver_with_mark};
|
||||
use swc_ecma_visit::{VisitMut, VisitMutWith};
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
/// Diff the output of swc minifier and terser.
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub(crate) struct DiffMinifiedCommand {
|
||||
input: PathBuf,
|
||||
|
||||
#[structopt(long)]
|
||||
no_mangle: bool,
|
||||
}
|
||||
|
||||
impl DiffMinifiedCommand {
|
||||
pub fn run(self, cm: Arc<SourceMap>) -> Result<()> {
|
||||
let terser_output = self
|
||||
.get_output_from_terser()
|
||||
.context("failed to get output from terser")?;
|
||||
|
||||
let fm = cm
|
||||
.load_file(&self.input)
|
||||
.context("failed to load input file")?;
|
||||
|
||||
let top_level_mark = Mark::fresh(Mark::root());
|
||||
|
||||
let mut swc_module = {
|
||||
let mut m = parse(&fm).context("failed to parse input file using swc")?;
|
||||
|
||||
m.visit_mut_with(&mut resolver_with_mark(top_level_mark));
|
||||
m.visit_mut_with(&mut fixer(None));
|
||||
m.visit_mut_with(&mut Normalizer::default());
|
||||
|
||||
m = swc_ecma_minifier::optimize(
|
||||
m,
|
||||
cm.clone(),
|
||||
None,
|
||||
None,
|
||||
&MinifyOptions {
|
||||
compress: Some(Default::default()),
|
||||
mangle: if self.no_mangle {
|
||||
None
|
||||
} else {
|
||||
Some(Default::default())
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
&ExtraOptions { top_level_mark },
|
||||
);
|
||||
|
||||
m.visit_mut_with(&mut fixer(None));
|
||||
m.visit_mut_with(&mut Normalizer::default());
|
||||
m.visit_mut_with(&mut BeforeDiffNormalizer::default());
|
||||
|
||||
m
|
||||
};
|
||||
|
||||
let terser_fm = cm.new_source_file(FileName::Anon, terser_output);
|
||||
let mut terser_module = parse(&terser_fm)?;
|
||||
|
||||
{
|
||||
terser_module.visit_mut_with(&mut resolver_with_mark(top_level_mark));
|
||||
terser_module.visit_mut_with(&mut fixer(None));
|
||||
terser_module.visit_mut_with(&mut Normalizer::default());
|
||||
terser_module.visit_mut_with(&mut BeforeDiffNormalizer::default());
|
||||
}
|
||||
{
|
||||
// Diff
|
||||
|
||||
let config = swc_ecma_diff::Config { ignore_span: true };
|
||||
let mut ctx = swc_ecma_diff::Ctx::new(config);
|
||||
let diff_res = swc_module.diff(&mut terser_module, &mut ctx);
|
||||
|
||||
eprintln!("Diff: \n{}", diff_res);
|
||||
}
|
||||
|
||||
swc_module.visit_mut_with(&mut Normalizer::default());
|
||||
terser_module.visit_mut_with(&mut Normalizer::default());
|
||||
|
||||
let swc_output = print_js(cm.clone(), &swc_module, None).context("failed to print js")?;
|
||||
let terser_output = print_js(cm, &terser_module, None).context("failed to print js")?;
|
||||
|
||||
if swc_output == terser_output {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut swc_file = NamedTempFile::new()?;
|
||||
|
||||
writeln!(swc_file, "{}", swc_output)?;
|
||||
|
||||
let mut terser_file = NamedTempFile::new()?;
|
||||
|
||||
writeln!(terser_file, "{}", terser_output)?;
|
||||
|
||||
Command::new("code")
|
||||
.arg("--wait")
|
||||
.arg("--diff")
|
||||
.arg(&swc_file.path())
|
||||
.arg(&terser_file.path())
|
||||
.status()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Invoke `terser`
|
||||
fn get_output_from_terser(&self) -> Result<String> {
|
||||
let mut c = Command::new("terser");
|
||||
|
||||
c.arg("--compress");
|
||||
|
||||
if !self.no_mangle {
|
||||
c.arg("--mangle");
|
||||
}
|
||||
|
||||
let output = c
|
||||
.arg("--")
|
||||
.arg(&self.input)
|
||||
.stderr(Stdio::inherit())
|
||||
.output()
|
||||
.context("failed to run terser")?;
|
||||
|
||||
if !output.status.success() {
|
||||
bail!("terser failed with status code {}", output.status);
|
||||
}
|
||||
|
||||
let src = String::from_utf8(output.stdout).context("failed to parse terser output")?;
|
||||
Ok(src)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Normalizer {}
|
||||
|
||||
impl VisitMut for Normalizer {
|
||||
fn visit_mut_new_expr(&mut self, e: &mut NewExpr) {
|
||||
e.visit_mut_children_with(self);
|
||||
|
||||
if let Some(args) = &e.args {
|
||||
if args.is_empty() {
|
||||
e.args = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_stmt(&mut self, s: &mut Stmt) {
|
||||
s.visit_mut_children_with(self);
|
||||
|
||||
if let Stmt::Decl(Decl::Var(v)) = s {
|
||||
if v.decls.is_empty() {
|
||||
s.take();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_stmts(&mut self, stmts: &mut Vec<Stmt>) {
|
||||
stmts.visit_mut_children_with(self);
|
||||
|
||||
stmts.retain(|s| !matches!(s, Stmt::Empty(..)));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct BeforeDiffNormalizer {}
|
||||
|
||||
impl VisitMut for BeforeDiffNormalizer {
|
||||
fn visit_mut_stmt(&mut self, s: &mut Stmt) {
|
||||
s.visit_mut_children_with(self);
|
||||
|
||||
if let Stmt::Block(bs) = s {
|
||||
if bs.stmts.len() == 1 {
|
||||
*s = bs.stmts[0].take();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_str(&mut self, s: &mut Str) {
|
||||
s.visit_mut_children_with(self);
|
||||
s.has_escape = false;
|
||||
}
|
||||
}
|
@ -1,131 +0,0 @@
|
||||
use crate::util::parse;
|
||||
use anyhow::{Context, Result};
|
||||
use rayon::prelude::*;
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use swc_common::{
|
||||
collections::AHashMap, comments::NoopComments, errors::HANDLER, FileName, SourceFile,
|
||||
SourceMap, GLOBALS,
|
||||
};
|
||||
use swc_ecma_loader::{resolve::Resolve, resolvers::node::NodeModulesResolver, TargetEnv};
|
||||
use tracing::info;
|
||||
|
||||
pub fn collect_deps(cm: Arc<SourceMap>, working_dir: &Path, entry: &Path) -> Result<Vec<PathBuf>> {
|
||||
let collector = DependencyCollector {
|
||||
cm,
|
||||
working_dir: working_dir.to_path_buf(),
|
||||
cache: Default::default(),
|
||||
resolver: Box::new(NodeModulesResolver::new(
|
||||
TargetEnv::Node,
|
||||
Default::default(),
|
||||
false,
|
||||
)),
|
||||
};
|
||||
|
||||
collector.load_recursively(Arc::new(FileName::Real(entry.to_path_buf())))?;
|
||||
|
||||
let files = collector.cache.into_inner()?;
|
||||
|
||||
Ok(files
|
||||
.into_iter()
|
||||
.map(|(_, file)| file.fm.name.clone())
|
||||
.filter_map(|f| match f {
|
||||
FileName::Real(v) => Some(v),
|
||||
_ => None,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
struct DependencyCollector {
|
||||
cm: Arc<SourceMap>,
|
||||
|
||||
#[allow(unused)]
|
||||
working_dir: PathBuf,
|
||||
|
||||
cache: Mutex<AHashMap<Arc<FileName>, Arc<ModuleData>>>,
|
||||
|
||||
resolver: Box<dyn Resolve>,
|
||||
}
|
||||
|
||||
impl DependencyCollector {
|
||||
fn load_recursively(&self, name: Arc<FileName>) -> Result<()> {
|
||||
if self.cache.lock().unwrap().contains_key(&name) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
info!("Loading {}", name);
|
||||
|
||||
let fm = match &*name {
|
||||
FileName::Real(path) => self.cm.load_file(path)?,
|
||||
FileName::Custom(..) => return Ok(()),
|
||||
_ => {
|
||||
todo!("load({:?})", name)
|
||||
}
|
||||
};
|
||||
|
||||
self.cache
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(name.clone(), Arc::new(ModuleData { fm: fm.clone() }));
|
||||
|
||||
if let FileName::Real(name) = &*name {
|
||||
if let Some(ext) = name.extension() {
|
||||
if ext == "json" {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let module = parse(&fm)?;
|
||||
|
||||
let deps = swc_ecma_dep_graph::analyze_dependencies(&module, &NoopComments);
|
||||
let deps = deps
|
||||
.into_iter()
|
||||
.filter(|dep| &*dep.specifier != "next")
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let _res = GLOBALS.with(|globals| {
|
||||
HANDLER.with(|handler| {
|
||||
deps.into_par_iter()
|
||||
.map(|dep| {
|
||||
GLOBALS.set(globals, || {
|
||||
HANDLER.set(handler, || {
|
||||
let res = self
|
||||
.resolver
|
||||
.resolve(&fm.name, &dep.specifier)
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed to resolve `{}` from `{}`",
|
||||
dep.specifier, fm.name
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok((res, dep))
|
||||
})
|
||||
})
|
||||
})
|
||||
.map(|res| {
|
||||
GLOBALS.set(globals, || {
|
||||
HANDLER.set(handler, || {
|
||||
res.and_then(|(name, dep)| {
|
||||
let name = Arc::new(name);
|
||||
self.load_recursively(name.clone()).with_context(|| {
|
||||
format!("failed to load `{}` (`{}`)", name, dep.specifier)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
.collect::<Result<_>>()
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct ModuleData {
|
||||
fm: Arc<SourceFile>,
|
||||
}
|
@ -1,241 +0,0 @@
|
||||
use self::{deps::collect_deps, types::ignore_typescript};
|
||||
use crate::util::{parse, print_js};
|
||||
use anyhow::{bail, Context, Result};
|
||||
use std::{
|
||||
path::PathBuf,
|
||||
process::{Command, Stdio},
|
||||
sync::Arc,
|
||||
};
|
||||
use structopt::StructOpt;
|
||||
use swc_common::{comments::NoopComments, Mark, SourceMap};
|
||||
use swc_ecma_minifier::option::{ExtraOptions, MinifyOptions};
|
||||
use swc_ecma_transforms_base::{
|
||||
fixer::fixer,
|
||||
helpers::{inject_helpers, Helpers, HELPERS},
|
||||
hygiene::hygiene,
|
||||
resolver::resolver_with_mark,
|
||||
};
|
||||
use swc_ecma_transforms_typescript::strip_with_jsx;
|
||||
use swc_ecma_visit::{FoldWith, VisitMutWith};
|
||||
use swc_node_comments::SwcComments;
|
||||
use swc_timer::timer;
|
||||
use tracing::{info, span, Level};
|
||||
|
||||
mod deps;
|
||||
mod types;
|
||||
|
||||
/// This tool repeat replacing one file with a minified form at a time.
|
||||
#[derive(Debug, StructOpt)]
|
||||
pub(crate) struct ReduceMinCommand {
|
||||
entry: PathBuf,
|
||||
|
||||
#[structopt(long)]
|
||||
working_dir: PathBuf,
|
||||
|
||||
/// This command is invoked using `bash`.
|
||||
#[structopt(long = "build")]
|
||||
build_command: String,
|
||||
|
||||
/// This command is invoked using `bash`.
|
||||
#[structopt(long = "test")]
|
||||
test_command: String,
|
||||
}
|
||||
|
||||
impl ReduceMinCommand {
|
||||
pub(crate) fn run(self, cm: Arc<SourceMap>) -> Result<()> {
|
||||
let all_files = {
|
||||
let _timer = timer!("collect list of files to patch");
|
||||
collect_deps(cm.clone(), &self.working_dir, &self.entry)?
|
||||
};
|
||||
|
||||
let mut runner = Runner {
|
||||
cm,
|
||||
comments: Default::default(),
|
||||
working_dir: self.working_dir,
|
||||
build_command: self.build_command,
|
||||
test_command: self.test_command,
|
||||
expected: Default::default(),
|
||||
};
|
||||
|
||||
{
|
||||
let _span = span!(Level::ERROR, "initial run, without minification").entered();
|
||||
runner.expected = runner.check().context("initial check failed")?;
|
||||
}
|
||||
|
||||
runner.run(all_files)
|
||||
}
|
||||
}
|
||||
|
||||
struct Runner {
|
||||
cm: Arc<SourceMap>,
|
||||
|
||||
comments: SwcComments,
|
||||
|
||||
working_dir: PathBuf,
|
||||
build_command: String,
|
||||
test_command: String,
|
||||
|
||||
expected: String,
|
||||
}
|
||||
|
||||
/// Restores original content on drop
|
||||
struct Patch {
|
||||
path: PathBuf,
|
||||
orig: Arc<String>,
|
||||
}
|
||||
|
||||
impl Drop for Patch {
|
||||
fn drop(&mut self) {
|
||||
let _ = std::fs::write(&self.path, self.orig.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
impl Runner {
|
||||
fn patch_file(&mut self, path: PathBuf) -> Result<(Patch, String)> {
|
||||
(|| -> Result<_> {
|
||||
let fm = self.cm.load_file(&path).context("failed to load file")?;
|
||||
|
||||
let top_level_mark = Mark::fresh(Mark::root());
|
||||
|
||||
let mut m = parse(&fm).context("failed to parse input file using swc")?;
|
||||
|
||||
let helpers = Helpers::new(false);
|
||||
|
||||
m = HELPERS.set(&helpers, || {
|
||||
let mut m = m;
|
||||
m.visit_mut_with(&mut resolver_with_mark(top_level_mark));
|
||||
m.visit_mut_with(&mut strip_with_jsx(
|
||||
self.cm.clone(),
|
||||
swc_ecma_transforms_typescript::Config {
|
||||
..Default::default()
|
||||
},
|
||||
NoopComments,
|
||||
top_level_mark,
|
||||
));
|
||||
m = m.fold_with(&mut swc_ecma_transforms_react::react(
|
||||
self.cm.clone(),
|
||||
None::<NoopComments>,
|
||||
Default::default(),
|
||||
top_level_mark,
|
||||
));
|
||||
|
||||
m = swc_ecma_minifier::optimize(
|
||||
m,
|
||||
self.cm.clone(),
|
||||
None,
|
||||
None,
|
||||
&MinifyOptions {
|
||||
compress: Some(Default::default()),
|
||||
mangle: None,
|
||||
..Default::default()
|
||||
},
|
||||
&ExtraOptions { top_level_mark },
|
||||
);
|
||||
|
||||
m.visit_mut_with(&mut inject_helpers());
|
||||
|
||||
m.visit_mut_with(&mut hygiene());
|
||||
m.visit_mut_with(&mut fixer(None));
|
||||
|
||||
if let Some(ext) = path.extension() {
|
||||
if ext == "tsx" || ext == "ts" {
|
||||
m.visit_mut_with(&mut ignore_typescript(self.comments.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
m
|
||||
});
|
||||
|
||||
let mut patched = print_js(self.cm.clone(), &m, Some(&self.comments))?;
|
||||
|
||||
// Ignore helpers injected by swc
|
||||
macro_rules! ignore {
|
||||
($s:expr) => {{
|
||||
patched = patched.replace($s, concat!("//@ts-ignore\n", $s));
|
||||
}};
|
||||
}
|
||||
|
||||
// TODO: Find better way
|
||||
ignore!("function _");
|
||||
ignore!("_extends = ");
|
||||
ignore!("return _extends.apply(this, arguments)");
|
||||
|
||||
std::fs::write(&path, patched.as_bytes()).context("failed to write patched content")?;
|
||||
|
||||
let patch = Patch {
|
||||
path: path.clone(),
|
||||
orig: fm.src.clone(),
|
||||
};
|
||||
|
||||
Ok((patch, patched))
|
||||
})()
|
||||
.with_context(|| format!("failed to patch file: {}", path.display()))
|
||||
}
|
||||
|
||||
fn run(mut self, files: Vec<PathBuf>) -> Result<()> {
|
||||
// Patch one file at a time.
|
||||
for file in files {
|
||||
if let Some(ext) = file.extension() {
|
||||
if ext == "json" {
|
||||
info!("Skipping json file");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let _span = span!(Level::ERROR, "patch", file = &*file.display().to_string()).entered();
|
||||
|
||||
let (_patch, patched) = self.patch_file(file.clone())?;
|
||||
|
||||
let actual = self.check().with_context(|| {
|
||||
format!(
|
||||
"test failed for `{}`\nPatched:\n{}",
|
||||
file.display(),
|
||||
patched
|
||||
)
|
||||
})?;
|
||||
|
||||
if actual != self.expected {
|
||||
bail!("expected: {}, actual: {}", self.expected, actual);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build, test, and grab the console output.
|
||||
fn check(&mut self) -> Result<String> {
|
||||
{
|
||||
info!("Building app");
|
||||
|
||||
let mut cmd = Command::new("bash");
|
||||
let status = cmd
|
||||
.current_dir(&self.working_dir)
|
||||
.arg("-c")
|
||||
.arg(&self.build_command)
|
||||
.status()
|
||||
.context("failed to run build command")?;
|
||||
|
||||
if !status.success() {
|
||||
bail!("build failed");
|
||||
}
|
||||
}
|
||||
|
||||
info!("Testing app");
|
||||
|
||||
let mut cmd = Command::new("bash");
|
||||
|
||||
let output = cmd
|
||||
.current_dir(&self.working_dir)
|
||||
.arg("-c")
|
||||
.arg(&self.test_command)
|
||||
.stderr(Stdio::inherit())
|
||||
.output()
|
||||
.context("failed to get test output")?;
|
||||
|
||||
if !output.status.success() {
|
||||
bail!("test failed");
|
||||
}
|
||||
|
||||
Ok(String::from_utf8_lossy(&output.stdout).to_string())
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
use swc_common::{
|
||||
comments::{Comment, CommentKind, Comments},
|
||||
Spanned, DUMMY_SP,
|
||||
};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_visit::{VisitMut, VisitMutWith};
|
||||
use swc_node_comments::SwcComments;
|
||||
|
||||
/// Add `@ts-ignore` to all statements.
|
||||
pub fn ignore_typescript(comments: SwcComments) -> impl VisitMut {
|
||||
AddTypes {
|
||||
comments,
|
||||
should_not_annotate_type: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
struct AddTypes {
|
||||
comments: SwcComments,
|
||||
|
||||
should_not_annotate_type: bool,
|
||||
}
|
||||
|
||||
impl VisitMut for AddTypes {
|
||||
///
|
||||
/// - `(this.foo)` => `(this as any).foo`
|
||||
fn visit_mut_member_expr(&mut self, e: &mut MemberExpr) {
|
||||
e.visit_mut_children_with(self);
|
||||
|
||||
if let Expr::This(..) = &*e.obj {
|
||||
e.obj = Expr::Paren(ParenExpr {
|
||||
span: DUMMY_SP,
|
||||
expr: Box::new(Expr::TsAs(TsAsExpr {
|
||||
span: DUMMY_SP,
|
||||
expr: e.obj.clone(),
|
||||
type_ann: any_type(),
|
||||
})),
|
||||
})
|
||||
.into();
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_stmt(&mut self, s: &mut Stmt) {
|
||||
s.visit_mut_children_with(self);
|
||||
|
||||
self.comments.add_leading(
|
||||
s.span().lo,
|
||||
Comment {
|
||||
kind: CommentKind::Line,
|
||||
span: DUMMY_SP,
|
||||
text: "@ts-ignore".into(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn visit_mut_var_decl_or_expr(&mut self, n: &mut VarDeclOrExpr) {
|
||||
let old = self.should_not_annotate_type;
|
||||
self.should_not_annotate_type = true;
|
||||
n.visit_mut_children_with(self);
|
||||
self.should_not_annotate_type = old;
|
||||
}
|
||||
|
||||
fn visit_mut_var_decl_or_pat(&mut self, n: &mut VarDeclOrPat) {
|
||||
let old = self.should_not_annotate_type;
|
||||
self.should_not_annotate_type = true;
|
||||
n.visit_mut_children_with(self);
|
||||
self.should_not_annotate_type = old;
|
||||
}
|
||||
|
||||
fn visit_mut_var_declarator(&mut self, v: &mut VarDeclarator) {
|
||||
v.visit_mut_children_with(self);
|
||||
|
||||
if !self.should_not_annotate_type {
|
||||
if let Pat::Ident(id) = &mut v.name {
|
||||
id.type_ann = Some(TsTypeAnn {
|
||||
span: DUMMY_SP,
|
||||
type_ann: any_type(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn any_type() -> Box<TsType> {
|
||||
Box::new(TsType::TsKeywordType(TsKeywordType {
|
||||
span: DUMMY_SP,
|
||||
kind: TsKeywordTypeKind::TsAnyKeyword,
|
||||
}))
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::sync::Arc;
|
||||
use swc_common::{comments::Comments, input::SourceFileInput, FileName, SourceFile, SourceMap};
|
||||
use swc_ecma_ast::{EsVersion, Module};
|
||||
use swc_ecma_parser::{lexer::Lexer, EsConfig, Parser, Syntax, TsConfig};
|
||||
use swc_ecma_utils::HANDLER;
|
||||
|
||||
pub(crate) fn parse(fm: &SourceFile) -> Result<Module> {
|
||||
let syntax = Syntax::Es(EsConfig {
|
||||
jsx: true,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let syntax = match &fm.name {
|
||||
FileName::Real(p) => match p.extension() {
|
||||
Some(ext) => {
|
||||
if ext == "tsx" {
|
||||
Syntax::Typescript(TsConfig {
|
||||
tsx: true,
|
||||
decorators: true,
|
||||
..Default::default()
|
||||
})
|
||||
} else if ext == "ts" {
|
||||
Syntax::Typescript(TsConfig {
|
||||
decorators: true,
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
syntax
|
||||
}
|
||||
}
|
||||
None => syntax,
|
||||
},
|
||||
_ => syntax,
|
||||
};
|
||||
|
||||
let lexer = Lexer::new(syntax, EsVersion::latest(), SourceFileInput::from(fm), None);
|
||||
|
||||
let mut parser = Parser::new_from(lexer);
|
||||
|
||||
parser.parse_module().map_err(|err| {
|
||||
HANDLER.with(|handler| {
|
||||
err.into_diagnostic(handler).emit();
|
||||
});
|
||||
|
||||
anyhow!("failed to parse module")
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn print_js(
|
||||
cm: Arc<SourceMap>,
|
||||
m: &Module,
|
||||
comments: Option<&dyn Comments>,
|
||||
) -> Result<String> {
|
||||
let mut buf = vec![];
|
||||
|
||||
{
|
||||
let wr = swc_ecma_codegen::text_writer::JsWriter::new(cm.clone(), "\n", &mut buf, None);
|
||||
let mut emitter = swc_ecma_codegen::Emitter {
|
||||
cfg: swc_ecma_codegen::Config { minify: false },
|
||||
cm,
|
||||
comments,
|
||||
wr,
|
||||
};
|
||||
|
||||
emitter.emit_module(m)?;
|
||||
}
|
||||
|
||||
Ok(String::from_utf8(buf)?)
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
[package]
|
||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||
description = "Diffing tools for ECMAScript"
|
||||
documentation = "https://rustdoc.swc.rs/swc_ecma_diff/"
|
||||
edition = "2021"
|
||||
include = ["Cargo.toml", "src/**/*.rs"]
|
||||
license = "Apache-2.0"
|
||||
name = "swc_ecma_diff"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.6.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[dependencies]
|
||||
ansi_term = "0.12.1"
|
||||
auto_impl = "0.5.0"
|
||||
num-bigint = "0.2"
|
||||
string_cache = "0.8"
|
||||
swc_atoms = {version = "0.2.9", path = "../swc_atoms"}
|
||||
swc_common = {version = "0.17.0", path = "../swc_common"}
|
||||
swc_ecma_ast = {version = "0.65.0", path = "../swc_ecma_ast"}
|
||||
|
||||
[dev-dependencies]
|
||||
swc_ecma_codegen = {version = "0.89.0", path = "../swc_ecma_codegen"}
|
||||
swc_ecma_parser = {version = "0.87.0", path = "../swc_ecma_parser"}
|
||||
testing = {version = "0.18.0", path = "../testing"}
|
@ -1,72 +0,0 @@
|
||||
use crate::{Config, Diff, DiffResult};
|
||||
use swc_atoms::JsWord;
|
||||
|
||||
/// The context for [Diff]. This contains config and path.
|
||||
///
|
||||
/// This is very inefficient, but it's fine as this will be used only for
|
||||
/// debugging tools. I don't want to bother with lifetimes.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Ctx {
|
||||
pub(crate) path: Vec<PathComponent>,
|
||||
pub(crate) config: Config,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum PathComponent {
|
||||
StructProp { struct_name: JsWord, key: JsWord },
|
||||
VecElem { l: usize, r: usize },
|
||||
}
|
||||
|
||||
impl Ctx {
|
||||
pub fn new(config: Config) -> Self {
|
||||
Ctx {
|
||||
path: Default::default(),
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn diff_struct<F>(&mut self, struct_name: &str, f: F) -> DiffResult
|
||||
where
|
||||
F: FnOnce(&mut StructDiffCtx),
|
||||
{
|
||||
let mut helper = StructDiffCtx {
|
||||
parent: self,
|
||||
results: Default::default(),
|
||||
struct_name: struct_name.into(),
|
||||
};
|
||||
f(&mut helper);
|
||||
|
||||
if helper.results.is_empty() {
|
||||
return DiffResult::Identical;
|
||||
}
|
||||
|
||||
DiffResult::Multiple(helper.results)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct StructDiffCtx<'a> {
|
||||
parent: &'a mut Ctx,
|
||||
results: Vec<DiffResult>,
|
||||
struct_name: JsWord,
|
||||
}
|
||||
|
||||
impl StructDiffCtx<'_> {
|
||||
pub fn field<T>(&mut self, field_name: &str, l: &mut T, r: &mut T)
|
||||
where
|
||||
T: Diff,
|
||||
{
|
||||
let mut ctx = self.parent.clone();
|
||||
ctx.path.push(PathComponent::StructProp {
|
||||
struct_name: self.struct_name.clone(),
|
||||
key: field_name.into(),
|
||||
});
|
||||
|
||||
let diff = l.diff(r, &mut ctx);
|
||||
|
||||
if matches!(diff, DiffResult::Identical) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.results.push(diff);
|
||||
}
|
||||
}
|
@ -1,166 +0,0 @@
|
||||
use crate::{Ctx, Diff, DiffResult, Node};
|
||||
use swc_common::EqIgnoreSpan;
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
diff_enum!(
|
||||
Expr,
|
||||
[
|
||||
This,
|
||||
Array,
|
||||
Object,
|
||||
Fn,
|
||||
Unary,
|
||||
Update,
|
||||
Bin,
|
||||
Assign,
|
||||
Member,
|
||||
SuperProp,
|
||||
Cond,
|
||||
Call,
|
||||
New,
|
||||
Seq,
|
||||
Ident,
|
||||
Lit,
|
||||
Tpl,
|
||||
TaggedTpl,
|
||||
Arrow,
|
||||
Class,
|
||||
Yield,
|
||||
MetaProp,
|
||||
Await,
|
||||
Paren,
|
||||
JSXMember,
|
||||
JSXNamespacedName,
|
||||
JSXEmpty,
|
||||
JSXElement,
|
||||
JSXFragment,
|
||||
TsTypeAssertion,
|
||||
TsConstAssertion,
|
||||
TsNonNull,
|
||||
TsAs,
|
||||
PrivateName,
|
||||
OptChain,
|
||||
Invalid
|
||||
]
|
||||
);
|
||||
|
||||
diff_enum!(PropOrSpread, [Prop, Spread]);
|
||||
diff_enum!(BlockStmtOrExpr, [BlockStmt, Expr]);
|
||||
diff_enum!(SuperProp, [Computed, Ident]);
|
||||
diff_enum!(Callee, [Expr, Super, Import]);
|
||||
diff_enum!(Lit, [Str, Bool, Null, BigInt, Num, Regex, JSXText]);
|
||||
diff_enum!(JSXAttrOrSpread, [JSXAttr, SpreadElement]);
|
||||
diff_enum!(Prop, [Shorthand, KeyValue, Assign, Getter, Setter, Method]);
|
||||
|
||||
diff_string_enum!(MetaPropKind);
|
||||
|
||||
diff_struct!(ThisExpr, [span]);
|
||||
diff_struct!(ArrayLit, [span, elems]);
|
||||
diff_struct!(ExprOrSpread, [spread, expr]);
|
||||
diff_struct!(ObjectLit, [span, props]);
|
||||
diff_struct!(FnExpr, [ident, function]);
|
||||
diff_struct!(UnaryExpr, [span, op, arg]);
|
||||
diff_struct!(Decorator, [span, expr]);
|
||||
diff_struct!(UpdateExpr, [span, prefix, op, arg]);
|
||||
diff_struct!(BinExpr, [span, op, left, right]);
|
||||
diff_struct!(AssignExpr, [span, op, left, right]);
|
||||
diff_struct!(MemberExpr, [span, obj, prop]);
|
||||
diff_struct!(SuperPropExpr, [span, obj, prop]);
|
||||
diff_enum!(MemberProp, [Ident, PrivateName, Computed]);
|
||||
diff_struct!(CondExpr, [span, test, cons, alt]);
|
||||
diff_struct!(CallExpr, [span, callee, args, type_args]);
|
||||
diff_struct!(NewExpr, [span, callee, args, type_args]);
|
||||
diff_struct!(SeqExpr, [span, exprs]);
|
||||
diff_struct!(TaggedTpl, [span, tag, tpl, type_params]);
|
||||
diff_struct!(TplElement, [span, cooked, raw, tail]);
|
||||
diff_struct!(Str, [span, value, has_escape, kind]);
|
||||
diff_struct!(
|
||||
ArrowExpr,
|
||||
[
|
||||
span,
|
||||
params,
|
||||
body,
|
||||
is_async,
|
||||
is_generator,
|
||||
type_params,
|
||||
return_type
|
||||
]
|
||||
);
|
||||
diff_struct!(ClassExpr, [ident, class]);
|
||||
diff_struct!(YieldExpr, [span, arg, delegate]);
|
||||
diff_struct!(MetaPropExpr, [span, kind]);
|
||||
diff_struct!(AwaitExpr, [span, arg]);
|
||||
diff_struct!(ParenExpr, [span, expr]);
|
||||
diff_struct!(JSXMemberExpr, [obj, prop]);
|
||||
diff_struct!(JSXNamespacedName, [ns, name]);
|
||||
diff_enum!(JSXObject, [JSXMemberExpr, Ident]);
|
||||
diff_struct!(JSXEmptyExpr, [span]);
|
||||
diff_struct!(JSXElement, [span, opening, children, closing]);
|
||||
diff_struct!(
|
||||
JSXOpeningElement,
|
||||
[span, name, attrs, self_closing, type_args]
|
||||
);
|
||||
diff_enum!(
|
||||
JSXElementChild,
|
||||
[
|
||||
JSXElement,
|
||||
JSXText,
|
||||
JSXSpreadChild,
|
||||
JSXExprContainer,
|
||||
JSXFragment
|
||||
]
|
||||
);
|
||||
diff_struct!(JSXAttr, [span, name, value]);
|
||||
diff_enum!(JSXAttrName, [Ident, JSXNamespacedName]);
|
||||
diff_enum!(
|
||||
JSXAttrValue,
|
||||
[Lit, JSXExprContainer, JSXElement, JSXFragment]
|
||||
);
|
||||
diff_struct!(JSXClosingElement, [span, name]);
|
||||
diff_struct!(JSXFragment, [span, opening, children, closing]);
|
||||
diff_enum!(JSXElementName, [Ident, JSXMemberExpr, JSXNamespacedName]);
|
||||
diff_struct!(JSXOpeningFragment, [span]);
|
||||
diff_struct!(JSXClosingFragment, [span]);
|
||||
diff_struct!(JSXSpreadChild, [span, expr]);
|
||||
diff_struct!(JSXExprContainer, [span, expr]);
|
||||
diff_enum!(JSXExpr, [JSXEmptyExpr, Expr]);
|
||||
diff_struct!(PrivateName, [span, id]);
|
||||
diff_struct!(OptChainExpr, [span, question_dot_token, expr]);
|
||||
diff_struct!(SpreadElement, [dot3_token, expr]);
|
||||
diff_struct!(Super, [span]);
|
||||
diff_struct!(Import, [span]);
|
||||
diff_struct!(Bool, [span, value]);
|
||||
diff_struct!(Null, [span]);
|
||||
diff_struct!(Number, [span, value]);
|
||||
diff_struct!(BigInt, [span, value]);
|
||||
diff_struct!(Regex, [span, exp, flags]);
|
||||
diff_struct!(JSXText, [span, value, raw]);
|
||||
diff_struct!(AssignProp, [key, value]);
|
||||
diff_struct!(KeyValueProp, [key, value]);
|
||||
diff_struct!(GetterProp, [span, key, type_ann, body]);
|
||||
diff_struct!(SetterProp, [span, key, param, body]);
|
||||
diff_struct!(MethodProp, [key, function]);
|
||||
diff_enum!(PropName, [Ident, Str, BigInt, Num, Computed]);
|
||||
diff_struct!(ComputedPropName, [span, expr]);
|
||||
diff_struct!(BindingIdent, [id, type_ann]);
|
||||
|
||||
/// Ignored
|
||||
impl Diff for StrKind {
|
||||
fn diff(&mut self, _: &mut Self, _: &mut Ctx) -> DiffResult {
|
||||
DiffResult::Identical
|
||||
}
|
||||
}
|
||||
|
||||
impl Diff for Tpl {
|
||||
fn diff(&mut self, other: &mut Self, ctx: &mut Ctx) -> DiffResult {
|
||||
if self.eq_ignore_span(&*other) {
|
||||
return crate::DiffResult::Identical;
|
||||
}
|
||||
|
||||
DiffResult::Different(crate::Difference {
|
||||
path: ctx.path.clone(),
|
||||
left: Node(format!("{:?}", self)),
|
||||
right: Node(format!("{:?}", other)),
|
||||
})
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
// TODO: Allow renaming of identifiers.
|
||||
|
||||
diff_struct!(Ident, [span, sym, optional]);
|
@ -1,15 +0,0 @@
|
||||
//! Implementation of [crate::Diff] for types in [swc_ecma_ast].
|
||||
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
mod expr;
|
||||
mod ident;
|
||||
mod ops;
|
||||
mod pat;
|
||||
mod stmt;
|
||||
mod typescript;
|
||||
|
||||
diff_struct!(Module, [span, body, shebang]);
|
||||
diff_struct!(Invalid, [span]);
|
||||
|
||||
diff_enum!(ModuleItem, [Stmt, ModuleDecl]);
|
@ -1,9 +0,0 @@
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
diff_string_enum!(AssignOp);
|
||||
diff_string_enum!(UnaryOp);
|
||||
diff_string_enum!(UpdateOp);
|
||||
diff_string_enum!(VarDeclKind);
|
||||
diff_string_enum!(BinaryOp);
|
||||
trivial!(MethodKind);
|
||||
trivial!(TsTypeOperatorOp);
|
@ -1,12 +0,0 @@
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
diff_enum!(Pat, [Ident, Rest, Array, Object, Invalid, Assign, Expr]);
|
||||
diff_enum!(PatOrExpr, [Pat, Expr]);
|
||||
diff_struct!(ArrayPat, [span, elems, optional, type_ann]);
|
||||
diff_struct!(ObjectPat, [span, props, optional, type_ann]);
|
||||
diff_struct!(RestPat, [span, dot3_token, arg, type_ann]);
|
||||
diff_struct!(AssignPat, [span, left, right, type_ann]);
|
||||
|
||||
diff_enum!(ObjectPatProp, [KeyValue, Assign, Rest]);
|
||||
diff_struct!(KeyValuePatProp, [key, value]);
|
||||
diff_struct!(AssignPatProp, [span, key, value]);
|
@ -1,188 +0,0 @@
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
diff_enum!(
|
||||
Stmt,
|
||||
[
|
||||
Block, Empty, Debugger, With, Return, Labeled, Break, Continue, If, Switch, Throw, Try,
|
||||
While, DoWhile, For, ForIn, ForOf, Decl, Expr
|
||||
]
|
||||
);
|
||||
|
||||
diff_struct!(BlockStmt, [span, stmts]);
|
||||
diff_struct!(EmptyStmt, [span]);
|
||||
diff_struct!(DebuggerStmt, [span]);
|
||||
diff_struct!(WithStmt, [span, obj, body]);
|
||||
diff_struct!(ReturnStmt, [span, arg]);
|
||||
diff_struct!(ThrowStmt, [span, arg]);
|
||||
diff_struct!(LabeledStmt, [span, label, body]);
|
||||
diff_struct!(BreakStmt, [span, label]);
|
||||
diff_struct!(ContinueStmt, [span, label]);
|
||||
diff_struct!(IfStmt, [span, test, cons, alt]);
|
||||
diff_struct!(SwitchStmt, [span, discriminant, cases]);
|
||||
diff_struct!(SwitchCase, [span, test, cons]);
|
||||
diff_struct!(TryStmt, [span, block, handler, finalizer]);
|
||||
diff_struct!(WhileStmt, [span, test, body]);
|
||||
diff_struct!(DoWhileStmt, [span, test, body]);
|
||||
diff_struct!(ForStmt, [span, init, test, update, body]);
|
||||
diff_struct!(ForInStmt, [span, left, right, body]);
|
||||
diff_struct!(ForOfStmt, [span, left, right, body, await_token]);
|
||||
diff_struct!(ExprStmt, [span, expr]);
|
||||
|
||||
diff_struct!(CatchClause, [span, param, body]);
|
||||
|
||||
diff_enum!(VarDeclOrPat, [VarDecl, Pat]);
|
||||
diff_enum!(VarDeclOrExpr, [VarDecl, Expr]);
|
||||
|
||||
diff_enum!(
|
||||
Decl,
|
||||
[Class, Fn, TsInterface, TsTypeAlias, TsEnum, TsModule, Var]
|
||||
);
|
||||
|
||||
diff_struct!(VarDecl, [span, kind, decls, declare]);
|
||||
diff_struct!(VarDeclarator, [span, name, init, definite]);
|
||||
diff_struct!(ClassDecl, [ident, class, declare]);
|
||||
diff_struct!(FnDecl, [ident, function, declare]);
|
||||
|
||||
diff_struct!(
|
||||
Function,
|
||||
[
|
||||
params,
|
||||
decorators,
|
||||
span,
|
||||
body,
|
||||
is_generator,
|
||||
is_async,
|
||||
type_params,
|
||||
return_type
|
||||
]
|
||||
);
|
||||
|
||||
diff_struct!(Param, [span, decorators, pat]);
|
||||
diff_struct!(
|
||||
Class,
|
||||
[
|
||||
span,
|
||||
decorators,
|
||||
body,
|
||||
super_class,
|
||||
is_abstract,
|
||||
type_params,
|
||||
super_type_params,
|
||||
implements
|
||||
]
|
||||
);
|
||||
diff_enum!(
|
||||
ClassMember,
|
||||
[
|
||||
Constructor,
|
||||
Method,
|
||||
PrivateMethod,
|
||||
ClassProp,
|
||||
PrivateProp,
|
||||
TsIndexSignature,
|
||||
Empty,
|
||||
StaticBlock
|
||||
]
|
||||
);
|
||||
diff_struct!(
|
||||
Constructor,
|
||||
[span, params, body, key, accessibility, is_optional]
|
||||
);
|
||||
diff_enum!(ParamOrTsParamProp, [Param, TsParamProp]);
|
||||
diff_enum!(
|
||||
ModuleDecl,
|
||||
[
|
||||
Import,
|
||||
ExportDecl,
|
||||
ExportNamed,
|
||||
ExportDefaultDecl,
|
||||
ExportDefaultExpr,
|
||||
ExportAll,
|
||||
TsImportEquals,
|
||||
TsExportAssignment,
|
||||
TsNamespaceExport
|
||||
]
|
||||
);
|
||||
diff_struct!(StaticBlock, [span, body]);
|
||||
diff_struct!(
|
||||
ClassMethod,
|
||||
[
|
||||
span,
|
||||
key,
|
||||
function,
|
||||
kind,
|
||||
is_static,
|
||||
accessibility,
|
||||
is_abstract,
|
||||
is_optional,
|
||||
is_override
|
||||
]
|
||||
);
|
||||
diff_struct!(
|
||||
PrivateMethod,
|
||||
[
|
||||
span,
|
||||
key,
|
||||
function,
|
||||
kind,
|
||||
is_static,
|
||||
accessibility,
|
||||
is_abstract,
|
||||
is_optional,
|
||||
is_override
|
||||
]
|
||||
);
|
||||
diff_struct!(
|
||||
ClassProp,
|
||||
[
|
||||
span,
|
||||
key,
|
||||
value,
|
||||
type_ann,
|
||||
is_static,
|
||||
is_abstract,
|
||||
is_optional,
|
||||
is_override,
|
||||
readonly,
|
||||
accessibility,
|
||||
decorators,
|
||||
declare,
|
||||
definite
|
||||
]
|
||||
);
|
||||
diff_struct!(
|
||||
PrivateProp,
|
||||
[
|
||||
span,
|
||||
key,
|
||||
value,
|
||||
type_ann,
|
||||
is_static,
|
||||
is_abstract,
|
||||
is_optional,
|
||||
is_override,
|
||||
readonly,
|
||||
computed,
|
||||
accessibility,
|
||||
decorators,
|
||||
definite
|
||||
]
|
||||
);
|
||||
diff_struct!(ImportDecl, [span, specifiers, src, type_only, asserts]);
|
||||
diff_struct!(ExportDecl, [span, decl]);
|
||||
diff_struct!(ExportDefaultDecl, [span, decl]);
|
||||
diff_struct!(ExportDefaultExpr, [span, expr]);
|
||||
diff_struct!(NamedExport, [span, specifiers, src, type_only, asserts]);
|
||||
diff_struct!(ExportAll, [span, src, asserts]);
|
||||
diff_enum!(ImportSpecifier, [Named, Default, Namespace]);
|
||||
diff_enum!(ExportSpecifier, [Named, Default, Namespace]);
|
||||
diff_enum!(DefaultDecl, [Class, Fn, TsInterfaceDecl]);
|
||||
diff_enum!(ModuleExportName, [Ident, Str]);
|
||||
|
||||
diff_struct!(ImportNamedSpecifier, [span, local, imported, is_type_only]);
|
||||
diff_struct!(ImportDefaultSpecifier, [span, local]);
|
||||
diff_struct!(ImportStarAsSpecifier, [span, local]);
|
||||
|
||||
diff_struct!(ExportNamedSpecifier, [span, orig, exported, is_type_only]);
|
||||
diff_struct!(ExportDefaultSpecifier, [exported]);
|
||||
diff_struct!(ExportNamespaceSpecifier, [span, name]);
|
@ -1,178 +0,0 @@
|
||||
use swc_ecma_ast::*;
|
||||
|
||||
diff_struct!(TsTypeAnn, [span, type_ann]);
|
||||
diff_struct!(TsTypeParamDecl, [span, params]);
|
||||
diff_struct!(TsTypeParam, [span, name, constraint, default]);
|
||||
|
||||
diff_enum!(
|
||||
TsType,
|
||||
[
|
||||
TsKeywordType,
|
||||
TsThisType,
|
||||
TsFnOrConstructorType,
|
||||
TsTypeRef,
|
||||
TsTypeQuery,
|
||||
TsTypeLit,
|
||||
TsArrayType,
|
||||
TsTupleType,
|
||||
TsOptionalType,
|
||||
TsRestType,
|
||||
TsUnionOrIntersectionType,
|
||||
TsConditionalType,
|
||||
TsInferType,
|
||||
TsParenthesizedType,
|
||||
TsTypeOperator,
|
||||
TsIndexedAccessType,
|
||||
TsMappedType,
|
||||
TsLitType,
|
||||
TsTypePredicate,
|
||||
TsImportType
|
||||
]
|
||||
);
|
||||
diff_enum!(TsEntityName, [TsQualifiedName, Ident]);
|
||||
diff_enum!(
|
||||
TsTypeElement,
|
||||
[
|
||||
TsCallSignatureDecl,
|
||||
TsConstructSignatureDecl,
|
||||
TsPropertySignature,
|
||||
TsGetterSignature,
|
||||
TsSetterSignature,
|
||||
TsMethodSignature,
|
||||
TsIndexSignature
|
||||
]
|
||||
);
|
||||
|
||||
diff_struct!(TsTypeParamInstantiation, [span, params]);
|
||||
diff_struct!(TsExprWithTypeArgs, [span, expr, type_args]);
|
||||
diff_struct!(TsAsExpr, [span, expr, type_ann]);
|
||||
diff_struct!(TsTypeAssertion, [span, expr, type_ann]);
|
||||
diff_struct!(TsConstAssertion, [span, expr]);
|
||||
diff_struct!(TsNonNullExpr, [span, expr]);
|
||||
diff_struct!(
|
||||
TsInterfaceDecl,
|
||||
[span, id, declare, type_params, extends, body]
|
||||
);
|
||||
diff_struct!(TsInterfaceBody, [span, body]);
|
||||
diff_struct!(TsTypeAliasDecl, [span, declare, id, type_params, type_ann]);
|
||||
diff_struct!(TsEnumDecl, [span, declare, id, members, is_const]);
|
||||
diff_struct!(TsEnumMember, [span, id, init]);
|
||||
diff_enum!(TsEnumMemberId, [Str, Ident]);
|
||||
diff_struct!(TsModuleDecl, [span, id, body, declare, global]);
|
||||
diff_enum!(TsModuleName, [Ident, Str]);
|
||||
diff_enum!(TsNamespaceBody, [TsModuleBlock, TsNamespaceDecl]);
|
||||
trivial!(Accessibility);
|
||||
diff_struct!(
|
||||
TsIndexSignature,
|
||||
[params, type_ann, readonly, is_static, span]
|
||||
);
|
||||
diff_enum!(TsFnParam, [Ident, Array, Object, Rest]);
|
||||
diff_struct!(
|
||||
TsParamProp,
|
||||
[
|
||||
span,
|
||||
decorators,
|
||||
accessibility,
|
||||
is_override,
|
||||
readonly,
|
||||
param
|
||||
]
|
||||
);
|
||||
diff_enum!(TsParamPropParam, [Ident, Assign]);
|
||||
diff_struct!(
|
||||
TsImportEqualsDecl,
|
||||
[span, declare, is_export, is_type_only, id, module_ref]
|
||||
);
|
||||
diff_enum!(TsModuleRef, [TsEntityName, TsExternalModuleRef]);
|
||||
diff_struct!(TsExportAssignment, [span, expr]);
|
||||
diff_struct!(TsNamespaceExportDecl, [id, span]);
|
||||
|
||||
diff_struct!(TsKeywordType, [span, kind]);
|
||||
trivial!(TsKeywordTypeKind);
|
||||
diff_struct!(TsThisType, [span]);
|
||||
diff_enum!(TsUnionOrIntersectionType, [TsUnionType, TsIntersectionType]);
|
||||
diff_enum!(TsFnOrConstructorType, [TsFnType, TsConstructorType]);
|
||||
diff_struct!(TsTypeRef, [span, type_name, type_params]);
|
||||
diff_struct!(TsTypeQuery, [span, expr_name]);
|
||||
diff_enum!(TsTypeQueryExpr, [TsEntityName, Import]);
|
||||
diff_struct!(TsTypeLit, [span, members]);
|
||||
diff_struct!(TsTupleType, [span, elem_types]);
|
||||
diff_struct!(TsTupleElement, [span, label, ty]);
|
||||
diff_struct!(TsArrayType, [span, elem_type]);
|
||||
diff_struct!(TsOptionalType, [span, type_ann]);
|
||||
diff_struct!(TsRestType, [span, type_ann]);
|
||||
diff_struct!(
|
||||
TsConditionalType,
|
||||
[span, check_type, extends_type, true_type, false_type]
|
||||
);
|
||||
diff_struct!(TsInferType, [span, type_param]);
|
||||
diff_struct!(TsParenthesizedType, [span, type_ann]);
|
||||
diff_struct!(TsTypeOperator, [span, op, type_ann]);
|
||||
diff_struct!(TsIndexedAccessType, [span, obj_type, index_type, readonly]);
|
||||
diff_struct!(
|
||||
TsMappedType,
|
||||
[span, readonly, type_param, type_ann, name_type, optional]
|
||||
);
|
||||
diff_struct!(TsLitType, [span, lit]);
|
||||
trivial!(TruePlusMinus);
|
||||
diff_enum!(TsLit, [Number, Str, Bool, BigInt, Tpl]);
|
||||
diff_struct!(TsTypePredicate, [span, param_name, asserts, type_ann]);
|
||||
diff_enum!(TsThisTypeOrIdent, [TsThisType, Ident]);
|
||||
diff_struct!(TsImportType, [span, arg, type_args, qualifier]);
|
||||
diff_struct!(TsQualifiedName, [left, right]);
|
||||
diff_struct!(TsUnionType, [span, types]);
|
||||
diff_struct!(TsIntersectionType, [span, types]);
|
||||
diff_struct!(TsFnType, [span, params, type_params, type_ann]);
|
||||
diff_struct!(
|
||||
TsConstructorType,
|
||||
[span, params, type_params, type_ann, is_abstract]
|
||||
);
|
||||
|
||||
diff_struct!(TsCallSignatureDecl, [span, params, type_ann, type_params]);
|
||||
diff_struct!(
|
||||
TsConstructSignatureDecl,
|
||||
[span, params, type_ann, type_params]
|
||||
);
|
||||
diff_struct!(
|
||||
TsPropertySignature,
|
||||
[
|
||||
span,
|
||||
key,
|
||||
type_ann,
|
||||
optional,
|
||||
readonly,
|
||||
computed,
|
||||
init,
|
||||
params,
|
||||
type_params
|
||||
]
|
||||
);
|
||||
diff_struct!(
|
||||
TsMethodSignature,
|
||||
[
|
||||
span,
|
||||
key,
|
||||
type_ann,
|
||||
params,
|
||||
type_params,
|
||||
readonly,
|
||||
computed,
|
||||
optional
|
||||
]
|
||||
);
|
||||
diff_struct!(
|
||||
TsGetterSignature,
|
||||
[span, readonly, key, computed, optional, type_ann]
|
||||
);
|
||||
diff_struct!(
|
||||
TsSetterSignature,
|
||||
[span, readonly, key, computed, optional, param]
|
||||
);
|
||||
|
||||
diff_struct!(TsNamespaceDecl, [span, declare, global, id, body]);
|
||||
|
||||
diff_struct!(TsModuleBlock, [span, body]);
|
||||
|
||||
diff_struct!(TsExternalModuleRef, [span, expr]);
|
||||
|
||||
diff_struct!(TsTplLitType, [span, types, quasis]);
|
@ -1,321 +0,0 @@
|
||||
pub use self::ctx::{Ctx, PathComponent};
|
||||
use std::fmt::{self, Debug, Display, Formatter};
|
||||
use swc_common::Span;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
mod ctx;
|
||||
mod js_ast;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Config {
|
||||
/// If this value is true, the differences of [Span]s are ignored.
|
||||
///
|
||||
/// Defaults to false.
|
||||
pub ignore_span: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Node(String);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Difference {
|
||||
pub path: Vec<PathComponent>,
|
||||
pub left: Node,
|
||||
pub right: Node,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum DiffResult {
|
||||
/// Two nodes are identical.
|
||||
Identical,
|
||||
|
||||
Different(Difference),
|
||||
|
||||
Multiple(Vec<DiffResult>),
|
||||
}
|
||||
|
||||
impl From<Difference> for DiffResult {
|
||||
fn from(v: Difference) -> Self {
|
||||
DiffResult::Different(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Difference {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Path: ")?;
|
||||
for c in &self.path {
|
||||
match c {
|
||||
PathComponent::StructProp { struct_name, key } => {
|
||||
write!(
|
||||
f,
|
||||
"({struct_name}.{key})",
|
||||
key = key,
|
||||
struct_name = struct_name
|
||||
)?;
|
||||
}
|
||||
PathComponent::VecElem { l, r } => {
|
||||
write!(f, "[{} <-> {}]", l, r)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
writeln!(f)?;
|
||||
|
||||
writeln!(f, "Left: {}", self.left.0)?;
|
||||
writeln!(f, "Right: {}", self.right.0)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DiffResult {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
DiffResult::Identical => {}
|
||||
DiffResult::Different(d) => {
|
||||
writeln!(f, "{}", d)?;
|
||||
}
|
||||
DiffResult::Multiple(d) => {
|
||||
if d.len() == 1 {
|
||||
for d in d {
|
||||
write!(f, "{}", d)?;
|
||||
}
|
||||
} else {
|
||||
for d in d {
|
||||
writeln!(f, "{}", d)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[auto_impl::auto_impl(Box)]
|
||||
pub trait Diff: Debug {
|
||||
/// This may remove common node from `self` and `other`.
|
||||
fn diff(&mut self, other: &mut Self, ctx: &mut Ctx) -> DiffResult;
|
||||
}
|
||||
|
||||
impl Diff for Span {
|
||||
fn diff(&mut self, other: &mut Self, ctx: &mut Ctx) -> DiffResult {
|
||||
if *self == *other {
|
||||
return DiffResult::Identical;
|
||||
}
|
||||
|
||||
if ctx.config.ignore_span {
|
||||
return DiffResult::Identical;
|
||||
}
|
||||
|
||||
DiffResult::Different(Difference {
|
||||
path: ctx.path.clone(),
|
||||
left: Node(format!("{:?}", self)),
|
||||
right: Node(format!("{:?}", other)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Diff for Vec<T>
|
||||
where
|
||||
T: Diff,
|
||||
{
|
||||
fn diff(&mut self, other: &mut Self, ctx: &mut Ctx) -> DiffResult {
|
||||
let mut results = Vec::new();
|
||||
|
||||
if self.len() != other.len() {
|
||||
results.push(DiffResult::Different(Difference {
|
||||
path: ctx.path.clone(),
|
||||
left: Node(format!("len = {}", self.len())),
|
||||
right: Node(format!("len = {}", other.len())),
|
||||
}));
|
||||
}
|
||||
|
||||
let mut should_retry = false;
|
||||
|
||||
let should_try_shifting = ctx.path.iter().all(|v| match v {
|
||||
PathComponent::VecElem { l, r } => *l == *r,
|
||||
_ => true,
|
||||
});
|
||||
|
||||
// If we are visiting this vector for the first time, we can shift indexes to
|
||||
// reduce output files.
|
||||
//
|
||||
// e.g.
|
||||
//
|
||||
// A:
|
||||
//
|
||||
// console.log('Foo')
|
||||
// console.log('Bar')
|
||||
// console.log('Baz')
|
||||
//
|
||||
//
|
||||
// B:
|
||||
//
|
||||
// console.log('Bar')
|
||||
// console.log('Baz')
|
||||
//
|
||||
//
|
||||
// We can remove two statements.
|
||||
if should_try_shifting {
|
||||
let mut l_removed = vec![];
|
||||
let mut r_removed = vec![];
|
||||
|
||||
for (l_idx, l) in self.iter_mut().enumerate() {
|
||||
for (r_idx, r) in other.iter_mut().enumerate() {
|
||||
if r_removed.contains(&r_idx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut ctx = ctx.clone();
|
||||
ctx.path.push(PathComponent::VecElem { l: l_idx, r: r_idx });
|
||||
|
||||
let diff = l.diff(r, &mut ctx);
|
||||
|
||||
if matches!(diff, DiffResult::Identical) {
|
||||
l_removed.push(l_idx);
|
||||
r_removed.push(r_idx);
|
||||
should_retry = true;
|
||||
break;
|
||||
}
|
||||
if l_idx == r_idx {
|
||||
results.push(diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !l_removed.is_empty() {
|
||||
let new_l = self
|
||||
.drain(..)
|
||||
.enumerate()
|
||||
.filter(|(i, _)| !l_removed.contains(i))
|
||||
.map(|(_, v)| v)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
*self = new_l;
|
||||
}
|
||||
|
||||
if !r_removed.is_empty() {
|
||||
let new_r = other
|
||||
.drain(..)
|
||||
.enumerate()
|
||||
.filter(|(i, _)| !r_removed.contains(i))
|
||||
.map(|(_, v)| v)
|
||||
.collect::<Vec<_>>();
|
||||
*other = new_r;
|
||||
}
|
||||
} else {
|
||||
let mut l = self.iter_mut();
|
||||
let mut r = other.iter_mut();
|
||||
let mut idx = 0;
|
||||
let mut removed = vec![];
|
||||
|
||||
while let (Some(l), Some(r)) = (l.next(), r.next()) {
|
||||
let cur_idx = idx;
|
||||
idx += 1;
|
||||
|
||||
let mut ctx = ctx.clone();
|
||||
ctx.path.push(PathComponent::VecElem {
|
||||
l: cur_idx,
|
||||
r: cur_idx,
|
||||
});
|
||||
|
||||
let diff = l.diff(r, &mut ctx);
|
||||
|
||||
if matches!(diff, DiffResult::Identical) {
|
||||
removed.push(cur_idx);
|
||||
continue;
|
||||
}
|
||||
|
||||
results.push(diff);
|
||||
}
|
||||
|
||||
if !removed.is_empty() {
|
||||
should_retry = true;
|
||||
|
||||
let new_l = self
|
||||
.drain(..)
|
||||
.enumerate()
|
||||
.filter(|(i, _)| !removed.contains(i))
|
||||
.map(|(_, v)| v)
|
||||
.collect::<Vec<_>>();
|
||||
let new_r = other
|
||||
.drain(..)
|
||||
.enumerate()
|
||||
.filter(|(i, _)| !removed.contains(i))
|
||||
.map(|(_, v)| v)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
*self = new_l;
|
||||
*other = new_r;
|
||||
}
|
||||
}
|
||||
|
||||
if should_retry {
|
||||
return self.diff(other, ctx);
|
||||
}
|
||||
|
||||
// TODO: Dump extra nodes
|
||||
|
||||
if results.is_empty() {
|
||||
return DiffResult::Identical;
|
||||
}
|
||||
|
||||
DiffResult::Multiple(results)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Diff for Option<T>
|
||||
where
|
||||
T: Diff,
|
||||
{
|
||||
fn diff(&mut self, other: &mut Self, ctx: &mut Ctx) -> DiffResult {
|
||||
let result = match (&mut *self, &mut *other) {
|
||||
(Some(l), Some(r)) => l.diff(r, ctx),
|
||||
(None, None) => DiffResult::Identical,
|
||||
(None, Some(r)) => DiffResult::Different(Difference {
|
||||
path: ctx.path.clone(),
|
||||
left: Node("None".into()),
|
||||
right: Node(format!("{:?}", r)),
|
||||
}),
|
||||
(Some(l), None) => DiffResult::Different(Difference {
|
||||
path: ctx.path.clone(),
|
||||
left: Node(format!("{:?}", l)),
|
||||
right: Node("None".into()),
|
||||
}),
|
||||
};
|
||||
|
||||
if matches!(result, DiffResult::Identical) {
|
||||
// Remove common node.
|
||||
*self = None;
|
||||
*other = None;
|
||||
return result;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
trivial!(bool, (), char);
|
||||
trivial!(usize, u8, u16, u32, u64, u128);
|
||||
trivial!(isize, i8, i16, i32, i64, i128);
|
||||
trivial!(f32, f64);
|
||||
trivial!(String, str);
|
||||
trivial!(num_bigint::BigInt);
|
||||
|
||||
impl<S> Diff for string_cache::Atom<S>
|
||||
where
|
||||
S: string_cache::StaticAtomSet,
|
||||
{
|
||||
fn diff(&mut self, other: &mut Self, ctx: &mut Ctx) -> DiffResult {
|
||||
if *self == *other {
|
||||
return DiffResult::Identical;
|
||||
}
|
||||
|
||||
DiffResult::Different(Difference {
|
||||
path: ctx.path.clone(),
|
||||
left: Node(format!("{:?}", self)),
|
||||
right: Node(format!("{:?}", other)),
|
||||
})
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
macro_rules! trivial {
|
||||
($T:ty) => {
|
||||
impl crate::Diff for $T {
|
||||
fn diff(&mut self, other: &mut Self, ctx: &mut crate::Ctx) -> crate::DiffResult {
|
||||
if *self == *other {
|
||||
return crate::DiffResult::Identical;
|
||||
}
|
||||
|
||||
crate::DiffResult::Different(crate::Difference {
|
||||
path: ctx.path.clone(),
|
||||
left: crate::Node(format!("{:?}", self)),
|
||||
right: crate::Node(format!("{:?}", other)),
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
$T:ty, $($tt:tt)*
|
||||
) => {
|
||||
trivial!($T);
|
||||
trivial!($($tt)*);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! diff_struct {
|
||||
(
|
||||
$T:ident,
|
||||
[
|
||||
$($field:ident),*
|
||||
]
|
||||
) => {
|
||||
impl crate::Diff for $T {
|
||||
fn diff(&mut self, other: &mut Self, ctx: &mut crate::Ctx) -> crate::DiffResult {
|
||||
// Ensure that we diff all fields.
|
||||
#[allow(unused)]
|
||||
fn _assert_all_fields(_node: &$T){
|
||||
let $T {
|
||||
$($field,)*
|
||||
} = _node;
|
||||
}
|
||||
use swc_common::EqIgnoreSpan;
|
||||
|
||||
|
||||
let result = ctx.diff_struct(stringify!($T), |ctx| {
|
||||
|
||||
$(
|
||||
ctx.field(stringify!($field), &mut self.$field, &mut other.$field);
|
||||
)*
|
||||
});
|
||||
|
||||
if ctx.config.ignore_span{
|
||||
if self.eq_ignore_span(&*other) {
|
||||
return crate::DiffResult::Identical;
|
||||
}
|
||||
} else {
|
||||
if *self == *other {
|
||||
return crate::DiffResult::Identical;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! diff_enum {
|
||||
(
|
||||
$T:ident,
|
||||
[
|
||||
$($Variant:ident),*
|
||||
]
|
||||
) => {
|
||||
impl crate::Diff for $T {
|
||||
fn diff(&mut self, other: &mut Self, ctx: &mut crate::Ctx) -> crate::DiffResult {
|
||||
// Ensure that we handle all variants.
|
||||
fn _assert_all_variants(_node: &$T){
|
||||
match _node {
|
||||
$(
|
||||
$T::$Variant(..) => {},
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
match (&mut *self, &mut * other) {
|
||||
$(
|
||||
(
|
||||
$T::$Variant(l),
|
||||
$T::$Variant(r),
|
||||
) => crate::Diff::diff(l,r,ctx),
|
||||
)*
|
||||
_ => crate::DiffResult::Different(crate::Difference {
|
||||
path: ctx.path.clone(),
|
||||
left: crate::Node(format!("{:?}", self)),
|
||||
right: crate::Node(format!("{:?}", other)),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! diff_string_enum {
|
||||
($T:ty) => {
|
||||
trivial!($T);
|
||||
};
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
import foo from 'foo;'
|
||||
|
||||
console.log(foo);
|
||||
console.log(foo);
|
@ -1 +0,0 @@
|
||||
console.log(foo);
|
@ -1,4 +0,0 @@
|
||||
Path: (Module.body)
|
||||
Left: len = 1
|
||||
Right: len = 0
|
||||
|
@ -1,3 +0,0 @@
|
||||
import foo from 'foo;'
|
||||
|
||||
console.log(foo);
|
@ -1,3 +0,0 @@
|
||||
console.log('Foo')
|
||||
console.log('Bar')
|
||||
console.log('Baz')
|
@ -1 +0,0 @@
|
||||
console.log('Foo');
|
@ -1,4 +0,0 @@
|
||||
Path: (Module.body)
|
||||
Left: len = 1
|
||||
Right: len = 0
|
||||
|
@ -1,2 +0,0 @@
|
||||
console.log('Bar')
|
||||
console.log('Baz')
|
@ -1,81 +0,0 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
use swc_common::{input::SourceFileInput, sync::Lrc, SourceMap};
|
||||
use swc_ecma_ast::Module;
|
||||
use swc_ecma_codegen::{
|
||||
text_writer::{JsWriter, WriteJs},
|
||||
Emitter,
|
||||
};
|
||||
use swc_ecma_diff::{Config, Ctx, Diff};
|
||||
use swc_ecma_parser::{lexer::Lexer, EsConfig, Parser, Syntax};
|
||||
use testing::NormalizedOutput;
|
||||
|
||||
fn parse(cm: Lrc<SourceMap>, path: &Path) -> Module {
|
||||
let fm = cm.load_file(path).unwrap();
|
||||
|
||||
let lexer = Lexer::new(
|
||||
Syntax::Es(EsConfig {
|
||||
jsx: true,
|
||||
..Default::default()
|
||||
}),
|
||||
Default::default(),
|
||||
SourceFileInput::from(&*fm),
|
||||
None,
|
||||
);
|
||||
|
||||
let mut parser = Parser::new_from(lexer);
|
||||
|
||||
parser.parse_module().unwrap()
|
||||
}
|
||||
|
||||
#[testing::fixture("tests/diff/**/l.js")]
|
||||
fn diff(l: PathBuf) {
|
||||
let r = l.with_file_name("r.js");
|
||||
let spec = l.with_file_name("output.swc-diff");
|
||||
|
||||
let (diff_str, l_str, r_str) = testing::run_test(false, |cm, _handler| {
|
||||
let mut l = parse(cm.clone(), &l);
|
||||
let mut r = parse(cm.clone(), &r);
|
||||
|
||||
let mut ctx = Ctx::new(Config { ignore_span: true });
|
||||
let res = l.diff(&mut r, &mut ctx);
|
||||
|
||||
let l = print(cm.clone(), &[l]);
|
||||
let r = print(cm, &[r]);
|
||||
|
||||
Ok((format!("{}", res), l, r))
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
NormalizedOutput::from(l_str)
|
||||
.compare_to_file(&l.with_file_name("l.output.js"))
|
||||
.unwrap();
|
||||
|
||||
NormalizedOutput::from(r_str)
|
||||
.compare_to_file(&r.with_file_name("r.output.js"))
|
||||
.unwrap();
|
||||
|
||||
NormalizedOutput::from(diff_str)
|
||||
.compare_to_file(&spec)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn print<N: swc_ecma_codegen::Node>(cm: Lrc<SourceMap>, nodes: &[N]) -> String {
|
||||
let mut buf = vec![];
|
||||
|
||||
{
|
||||
let wr: Box<dyn WriteJs> = Box::new(JsWriter::new(cm.clone(), "\n", &mut buf, None));
|
||||
|
||||
let mut emitter = Emitter {
|
||||
cfg: swc_ecma_codegen::Config { minify: false },
|
||||
cm,
|
||||
comments: None,
|
||||
wr,
|
||||
};
|
||||
|
||||
for n in nodes {
|
||||
n.emit_with(&mut emitter).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
String::from_utf8(buf).unwrap()
|
||||
}
|
Loading…
Reference in New Issue
Block a user