liblingo/backends/
npm.rs

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)?; // path after src
83            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)?; // path after src
102
103            let path = app
104                .output_root
105                .join("src-gen")
106                .join(src_postfix)
107                .join("dist")
108                .join(file_name + ".js");
109
110            // cleanup: rename executable to match the app name
111            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}