1use std::{
2 fs,
3 path::{Path, PathBuf},
4 process::{Command, Stdio},
5 sync::Arc,
6};
7
8use anyhow::{bail, Context, Result};
9use clap::{Args, Subcommand};
10use swc_common::SourceMap;
11use swc_ecma_minifier::option::MinifyOptions;
12use swc_ecma_transforms_base::fixer::fixer;
13use swc_ecma_visit::VisitMutWith;
14use swc_timer::timer;
15use tracing::info;
16
17use crate::{bundle::bundle, util::print_js};
18
19#[derive(Debug, Subcommand)]
22pub enum ExecForTestingCommand {
23 MinifiedBundle(TestMinifiedBundleCommand),
24}
25
26impl ExecForTestingCommand {
27 pub fn run(self, cm: Arc<SourceMap>) -> Result<()> {
28 let _timer = timer!("test");
29
30 let output = {
31 let _timer = timer!("process");
32
33 match self {
34 ExecForTestingCommand::MinifiedBundle(cmd) => cmd.run(cm),
35 }?
36 };
37
38 {
39 let _timer = timer!("run");
40 let stdout = output
41 .runtime
42 .execute(&output.path)
43 .context("failed to execute generated code")?;
44
45 info!("----- Stdout -----\n{}", stdout);
46 }
47
48 Ok(())
49 }
50}
51
52#[derive(Debug, Args)]
53pub struct TestMinifiedBundleCommand {
54 entry: String,
55}
56
57impl TestMinifiedBundleCommand {
58 fn run(self, cm: Arc<SourceMap>) -> Result<Output> {
59 let bundle = bundle(cm.clone(), &self.entry)?;
60
61 let mut minified = {
62 let _timer = timer!("minify");
63 swc_ecma_minifier::optimize(
64 bundle.module.into(),
65 cm.clone(),
66 None,
67 None,
68 &MinifyOptions {
69 compress: Some(Default::default()),
70 mangle: Some(Default::default()),
71 ..Default::default()
72 },
73 &swc_ecma_minifier::option::ExtraOptions {
74 unresolved_mark: bundle.unresolved_mark,
75 top_level_mark: bundle.top_level_mark,
76 mangle_name_cache: None,
77 },
78 )
79 .expect_module()
80 };
81
82 minified.visit_mut_with(&mut fixer(None));
83
84 let code = print_js(cm, &minified, true).context("failed to convert ast to code")?;
85
86 let path = Path::new("output.js").to_path_buf();
87 fs::write(&path, code.as_bytes()).context("failed to write code as file")?;
88
89 Ok(Output {
90 path,
91 runtime: JsRuntime::Deno,
92 })
93 }
94}
95
96pub struct Output {
97 pub path: PathBuf,
98 pub runtime: JsRuntime,
99}
100
101pub enum JsRuntime {
102 Deno,
104}
105
106impl JsRuntime {
107 pub fn execute(&self, path: &Path) -> Result<String> {
108 match self {
109 JsRuntime::Deno => {
111 let mut cmd = Command::new("deno");
112 cmd.arg("run").arg("--no-check");
113
114 cmd.arg(path);
115
116 cmd.stderr(Stdio::inherit());
117
118 let output = cmd.output().context("failed to get output from deno")?;
119
120 if !output.status.success() {
121 bail!("deno exited with status {}", output.status);
122 }
123
124 Ok(
125 (String::from_utf8(output.stdout).context("deno emitted non-utf8 string")?)
126 .trim()
127 .to_string(),
128 )
129 }
130 }
131 }
132}