nestrs-cli-rs 0.1.0

Rust port of the Nest CLI for the nestrs organization.
Documentation
//! Upstream source: `../nest-cli/lib/compiler/defaults/webpack-defaults.ts`.

use std::path::{Path, PathBuf};

use crate::configuration::DEFAULT_TSCONFIG_FILENAME;

use super::super::helpers::append_extension::append_ts_extension;
use super::super::plugins::plugins_loader::MultiNestCompilerPlugins;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct WebpackDefaults {
    pub entry: PathBuf,
    pub devtool: Option<String>,
    pub target: String,
    pub output_filename: PathBuf,
    pub ignore_warnings_except: String,
    pub externals: Vec<String>,
    pub externals_presets_node: bool,
    pub module_rules: Vec<WebpackModuleRule>,
    pub resolve_extensions: Vec<String>,
    pub resolve_plugins: Vec<WebpackResolvePlugin>,
    pub mode: String,
    pub optimization_node_env: bool,
    pub node_filename: bool,
    pub node_dirname: bool,
    pub plugins: Vec<WebpackPluginPlan>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct WebpackModuleRule {
    pub test: String,
    pub loader: String,
    pub transpile_only: bool,
    pub config_file: PathBuf,
    pub custom_transformers: bool,
    pub exclude: String,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum WebpackResolvePlugin {
    TsconfigPaths { config_file: PathBuf },
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum WebpackPluginPlan {
    IgnoreLazyImports { imports: Vec<String> },
    ForkTsChecker { config_file: PathBuf },
}

pub fn webpack_defaults_factory(
    source_root: impl AsRef<Path>,
    relative_source_root: impl AsRef<Path>,
    entry_filename: &str,
    is_debug_enabled: bool,
    ts_config_file: Option<impl AsRef<Path>>,
    plugins: &MultiNestCompilerPlugins,
) -> WebpackDefaults {
    let ts_config_file = ts_config_file
        .map(|path| path.as_ref().to_path_buf())
        .unwrap_or_else(|| PathBuf::from(DEFAULT_TSCONFIG_FILENAME));
    let is_plugin_registered = plugins.is_any_plugin_registered();
    let mut plugin_plans = vec![WebpackPluginPlan::IgnoreLazyImports {
        imports: vec![
            "@nestjs/microservices".to_string(),
            "@nestjs/microservices/microservices-module".to_string(),
            "@nestjs/websockets/socket-module".to_string(),
            "class-validator".to_string(),
            "class-transformer".to_string(),
            "class-transformer/storage".to_string(),
        ],
    }];

    if !is_plugin_registered {
        plugin_plans.push(WebpackPluginPlan::ForkTsChecker {
            config_file: ts_config_file.clone(),
        });
    }

    WebpackDefaults {
        entry: PathBuf::from(append_ts_extension(
            &source_root.as_ref().join(entry_filename).to_string_lossy(),
        )),
        devtool: is_debug_enabled.then(|| "inline-source-map".to_string()),
        target: "node".to_string(),
        output_filename: relative_source_root
            .as_ref()
            .join(format!("{entry_filename}.js")),
        ignore_warnings_except: "CriticalDependenciesWarning".to_string(),
        externals: vec!["webpack-node-externals".to_string()],
        externals_presets_node: true,
        module_rules: vec![WebpackModuleRule {
            test: ".tsx?$".to_string(),
            loader: "ts-loader".to_string(),
            transpile_only: !is_plugin_registered,
            config_file: ts_config_file.clone(),
            custom_transformers: is_plugin_registered,
            exclude: "node_modules".to_string(),
        }],
        resolve_extensions: vec![".tsx".to_string(), ".ts".to_string(), ".js".to_string()],
        resolve_plugins: vec![WebpackResolvePlugin::TsconfigPaths {
            config_file: ts_config_file,
        }],
        mode: "none".to_string(),
        optimization_node_env: false,
        node_filename: false,
        node_dirname: false,
        plugins: plugin_plans,
    }
}