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 filename
(String): The name of the modulemodulePath
(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
- Plugin: Learn about the Plugin API
- Compiler: Learn about the Compiler API
- Compilation: Learn about the Compilation API