1use crate::util::errors::LingoError;
2use crate::util::execute_command_to_build_result;
3use std::error::Error;
4use std::ffi::OsString;
5use std::fs;
6use std::path::{Path, PathBuf};
7use std::process::Command;
8
9pub struct Npm;
10
11pub struct TypeScriptToolCommands {
12 pub binary_name: &'static str,
13 pub install_command: &'static str,
14 pub release_build_argument: &'static str,
15}
16
17use crate::backends::{
18 BatchBackend, BatchBuildResults, BuildCommandOptions, BuildProfile, CommandSpec,
19};
20
21pub fn do_typescript_build(
22 results: &mut BatchBuildResults,
23 options: &BuildCommandOptions,
24 commands: TypeScriptToolCommands,
25) {
26 results.keep_going(options.keep_going);
27 super::lfc::LFC::do_parallel_lfc_codegen(options, results, false);
28 if !options.compile_target_code {
29 return;
30 }
31
32 let extract_name = |path: &PathBuf| -> Result<String, Box<dyn Error + Send + Sync>> {
33 match Path::new(&path).file_stem() {
34 Some(value) => value
35 .to_str()
36 .map(String::from)
37 .ok_or(Box::new(LingoError::InvalidMainReactor)),
38 None => Err(Box::new(LingoError::InvalidMainReactor)),
39 }
40 };
41
42 let extract_location = |main_reactor: &PathBuf,
43 root_path: &PathBuf|
44 -> Result<PathBuf, Box<dyn Error + Send + Sync>> {
45 let output_dir = main_reactor.strip_prefix(root_path)?;
46
47 let src_index = output_dir
48 .iter()
49 .map(|x| x.to_os_string())
50 .position(|element| element == *"src")
51 .ok_or(LingoError::InvalidMainReactor)?;
52
53 let mut path_copy: Vec<OsString> = output_dir.iter().map(|x| x.to_os_string()).collect();
54 path_copy.drain(0..src_index + 1);
55
56 let mut new_path_buf: PathBuf = PathBuf::new();
57
58 for element in path_copy {
59 new_path_buf.push(element);
60 }
61
62 new_path_buf.set_extension("");
63 Ok(new_path_buf)
64 };
65
66 results
67 .map(|app| {
68 let src_postfix = extract_location(&app.main_reactor, &app.root_path)?;
69 let path = app.output_root.join("src-gen").join(src_postfix);
70
71 let mut npm_install = Command::new(commands.binary_name);
72 npm_install.current_dir(path);
73 npm_install.arg(commands.install_command);
74 if options.profile == BuildProfile::Release {
75 npm_install.arg(commands.release_build_argument);
76 }
77
78 execute_command_to_build_result(npm_install)?;
79 Ok(())
80 })
81 .map(|app| {
82 let src_postfix = extract_location(&app.main_reactor, &app.root_path)?; let path = app.output_root.join("src-gen").join(src_postfix);
84
85 let mut npm_build = Command::new(commands.binary_name);
86 npm_build.current_dir(path);
87 npm_build.arg("run");
88 npm_build.arg("build");
89
90 if options.profile == BuildProfile::Release {
91 npm_build.arg(commands.release_build_argument);
92 }
93
94 execute_command_to_build_result(npm_build)?;
95
96 Ok(())
97 })
98 .map(|app| {
99 fs::create_dir_all(app.output_root.join("bin"))?;
100 let file_name = extract_name(&app.main_reactor)?;
101 let src_postfix = extract_location(&app.main_reactor, &app.root_path)?; let path = app
104 .output_root
105 .join("src-gen")
106 .join(src_postfix)
107 .join("dist")
108 .join(file_name + ".js");
109
110 fs::rename(path, app.executable_path())?;
112 Ok(())
113 });
114}
115
116impl BatchBackend for Npm {
117 fn execute_command(&mut self, command: &CommandSpec, results: &mut BatchBuildResults) {
118 match command {
119 CommandSpec::Build(options) => do_typescript_build(
120 results,
121 options,
122 TypeScriptToolCommands {
123 binary_name: "npm",
124 install_command: "install",
125 release_build_argument: "--production",
126 },
127 ),
128 CommandSpec::Clean => {
129 results.par_map(|app| {
130 crate::util::default_build_clean(&app.output_root)?;
131 crate::util::delete_subdirs(&app.output_root, &["node_modules", "dist"])?;
132 Ok(())
133 });
134 }
135 _ => todo!(),
136 }
137 }
138}