Skip to content

Loader API

Loaders are transformations that are applied to the source code of a module. They allow you to pre-process files as you import or "load" them.

Creating a Loader

A loader is a JavaScript function that takes the source code as input and returns the transformed source code.

javascript
module.exports = function(source, name, modulePath) {
  // Transform the source code
  const transformedSource = source.replace('Hello', 'Hello from Loader');
  
  return transformedSource;
};

Parameters

  • source (String): The content of the file
  • name (String): The name of the module
  • modulePath (String): The path of the module

Returns

  • String: The transformed source code

Loader Interface

The Loader struct represents a loader in the mini-rspack system.

rust
#[napi(object)]
pub struct Loader {
    pub path: String,
}

Properties

path

The path property is a string that represents the path of the loader.

javascript
console.log(loader.path);

Implementation Details

rust
#[napi(object)]
pub struct Loader {
    pub path: String,
}

Methods

new(path)

Creates a new loader with the given path.

javascript
const loader = new Loader('./loaders/babel-loader.js');

Parameters

  • path (String): The path of the loader

Returns

  • Loader: A new loader instance

Implementation Details

rust
impl Loader {
    pub fn new(path: String) -> Self {
        Self { path }
    }
}

Loader Runner

The loader runner is responsible for applying loaders to modules.

find_matching_loaders

Finds loaders that match the given module path.

rust
pub fn find_matching_loaders(module_path: &Path, rules: &Vec<RuleOptions>) -> Vec<Loader> {
    let mut loaders = Vec::new();
    
    for rule in rules {
        let test = &rule.test;
        let module_path_str = module_path.to_string_lossy();
        
        if module_path_str.contains(test) {
            for loader_path in &rule.use_ {
                loaders.push(Loader {
                    path: loader_path.clone(),
                });
            }
        }
    }
    
    loaders
}

apply_loaders

Applies loaders to the given source code.

rust
pub fn apply_loaders(source: &str, loaders: &Vec<Loader>, module_path: &Path) -> Result<String> {
    // If no loaders, return the source as is
    if loaders.is_empty() {
        return Ok(source.to_string());
    }

    // Create a temporary file for the loader runner
    let mut temp_file = tempfile::NamedTempFile::new()?;
    let loader_runner_path = temp_file.path();

    // Generate the loader runner code
    let loader_runner_code = generate_loader_runner(source, loaders, module_path)?;

    // Write the loader runner code to the temporary file
    std::fs::write(loader_runner_path, loader_runner_code)?;

    // Execute the loader runner
    let output = std::process::Command::new("node")
        .arg(loader_runner_path)
        .output()?;

    // Check if the execution was successful
    if !output.status.success() {
        let error_message = String::from_utf8_lossy(&output.stderr);
        return Err(anyhow::anyhow!("Loader execution failed: {}", error_message));
    }

    // Get the transformed source from the output
    let transformed_source = String::from_utf8_lossy(&output.stdout).to_string();

    Ok(transformed_source)
}

generate_loader_runner

Generates the loader runner code.

rust
fn generate_loader_runner(source: &str, loaders: &Vec<Loader>, module_path: &Path) -> Result<String> {
    let module_path_str = module_path.to_string_lossy();
    let escaped_source = escape_js_string(source);
    
    let mut loader_requires = String::new();
    let mut loader_calls = String::new();
    
    for (i, loader) in loaders.iter().enumerate() {
        let loader_var = format!("loader{}", i);
        loader_requires.push_str(&format!("const {} = require('{}');\n", loader_var, loader.path));
        
        if i == 0 {
            loader_calls.push_str(&format!("let result = {}('{}', '{}', '{}');\n", 
                                          loader_var, escaped_source, "", module_path_str));
        } else {
            loader_calls.push_str(&format!("result = {}(result, '{}', '{}');\n", 
                                          loader_var, "", module_path_str));
        }
    }
    
    let runner_code = format!(
        r#"
        // Loader runner generated by mini-rspack
        {}
        
        // Execute loaders
        {}
        
        // Output the result
        console.log(result);
        "#,
        loader_requires,
        loader_calls
    );
    
    Ok(runner_code)
}

Next Steps

Released under the MIT License.