Skip to content

Commit 836f05c

Browse files
authored
Merge pull request #28 from metacall/docs/metassr-bundler
docs: document metassr-bundler
2 parents 637d55a + 58a7343 commit 836f05c

File tree

2 files changed

+100
-58
lines changed

2 files changed

+100
-58
lines changed

crates/metassr-bundler/src/bundle.js

Lines changed: 61 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,135 @@
11
const { rspack } = require('@rspack/core');
22
const path = require('path');
33

4+
/**
5+
* Safely parses a JSON string, returning undefined if parsing fails.
6+
* @param {string} json - The JSON string to parse.
7+
* @returns {Object|undefined} - Parsed object or undefined if parsing fails.
8+
*/
49
function safelyParseJSON(json) {
510
try {
6-
return JSON.parse(json)
11+
return JSON.parse(json);
712
} catch (_) {
8-
return undefined
13+
return undefined;
914
}
1015
}
1116

17+
// Default configuration object for rspack bundling process
1218
let config = {
13-
1419
output: {
15-
filename: '[name].js',
20+
filename: '[name].js', // Output filename with the entry name
1621
library: {
17-
type: 'commonjs2',
22+
type: 'commonjs2', // Set library type to CommonJS2 (Node.js modules)
1823
},
19-
publicPath: ''
24+
publicPath: '' // Specify the base path for all assets within the application
2025
},
2126
resolve: {
22-
extensions: ['.js', '.jsx', '.tsx', '.ts']
27+
extensions: ['.js', '.jsx', '.tsx', '.ts'] // Extensions that will be resolved
2328
},
2429
optimization: {
25-
minimize: false,
30+
minimize: false, // Disable minimization for easier debugging
2631
},
2732
module: {
2833
rules: [
2934
{
30-
test: /\.(jsx|js)$/,
31-
exclude: /node_modules/,
35+
test: /\.(jsx|js)$/, // Rule for JavaScript and JSX files
36+
exclude: /node_modules/, // Exclude node_modules directory
3237
use: {
33-
loader: 'builtin:swc-loader',
38+
loader: 'builtin:swc-loader', // Use the SWC loader to transpile ES6+ and JSX
3439
options: {
35-
sourceMap: true,
40+
sourceMap: true, // Enable source maps for easier debugging
3641
jsc: {
3742
parser: {
38-
syntax: 'ecmascript',
39-
jsx: true,
43+
syntax: 'ecmascript', // Set parser syntax to ECMAScript
44+
jsx: true, // Enable parsing JSX syntax
4045
},
41-
externalHelpers: false,
42-
preserveAllComments: false,
46+
externalHelpers: false, // Disable external helpers (use inline helpers)
47+
preserveAllComments: false, // Remove comments from output
4348
transform: {
4449
react: {
45-
runtime: 'automatic',
46-
throwIfNamespace: true,
47-
useBuiltins: false,
50+
runtime: 'automatic', // Use React's automatic JSX runtime
51+
throwIfNamespace: true, // Throw error if namespace is used
52+
useBuiltins: false, // Don't include built-in polyfills
4853
},
4954
},
5055
},
5156
},
52-
5357
},
54-
type: 'javascript/auto',
58+
type: 'javascript/auto', // Specify the type as auto (for backward compatibility)
5559
},
5660
{
57-
test: /\.(tsx|ts)$/,
58-
exclude: /node_modules/,
61+
test: /\.(tsx|ts)$/, // Rule for TypeScript and TSX files
62+
exclude: /node_modules/, // Exclude node_modules directory
5963
use: {
60-
loader: 'builtin:swc-loader',
64+
loader: 'builtin:swc-loader', // Use the SWC loader to transpile TS and TSX
6165
options: {
6266
jsc: {
6367
parser: {
64-
syntax: 'typescript',
65-
tsx: true,
68+
syntax: 'typescript', // Set parser syntax to TypeScript
69+
tsx: true, // Enable parsing TSX syntax
6670
},
6771
transform: {
6872
react: {
69-
runtime: 'automatic',
70-
throwIfNamespace: true,
71-
useBuiltins: false,
73+
runtime: 'automatic', // Use React's automatic JSX runtime
74+
throwIfNamespace: true, // Throw error if namespace is used
75+
useBuiltins: false, // Don't include built-in polyfills
7276
},
7377
},
7478
},
7579
},
7680
},
77-
type: 'javascript/auto',
81+
type: 'javascript/auto', // Specify the type as auto
7882
},
7983
{
80-
test: /\.(png|svg|jpg)$/,
81-
type: 'asset/inline',
84+
test: /\.(png|svg|jpg)$/, // Rule for image files (PNG, SVG, JPG)
85+
type: 'asset/inline', // Inline assets as Base64 strings
8286
},
8387
],
88+
},
89+
};
8490

85-
}
86-
}
87-
91+
/**
92+
* Bundles web resources using rspack.
93+
* @param {Object|string} entry - The entry point(s) for the bundling process (can be a string or JSON object).
94+
* @param {string} dist - The distribution path where bundled files will be output.
95+
* @returns {Promise} - Resolves when bundling is successful, rejects if there is an error.
96+
*/
8897
async function web_bundling(entry, dist) {
89-
98+
// Create a bundler instance using the config and parameters
9099
const compiler = rspack(
91100
{
92-
...config,
93-
entry: safelyParseJSON(entry) ?? entry,
101+
...config, // Merge with the default config
102+
entry: safelyParseJSON(entry) ?? entry, // Parse entry if it's JSON, otherwise use it as is
94103
output: dist ? {
95104
...config.output,
96-
path: path.join(process.cwd(), dist)
105+
path: path.join(process.cwd(), dist), // Use current working directory and output path
97106
} : config.output,
98-
99-
name: 'Client',
100-
mode: 'development',
101-
devtool: 'source-map',
102-
stats: { preset: 'errors-warnings', timings: true, colors: true },
103-
target: 'web',
107+
// minimize: true,
108+
name: 'Client', // Name of the bundle (Client)
109+
mode: 'production', // Set mode to development (for non-minimized builds)
110+
devtool: 'source-map', // Enable source maps for better debugging
111+
stats: { preset: 'errors-warnings', timings: true, colors: true }, // Customize bundling stats output
112+
target: 'web', // Set the target environment to web (for browser usage)
104113
}
105-
106114
);
107115

116+
// Return a promise that runs the bundling process and resolves or rejects based on the result
108117
return new Promise((resolve, reject) => {
109118
return compiler.run((error, stats) => {
119+
// Handle errors during the bundling process
110120
if (error) {
111-
reject(error.message);
121+
reject(error.message); // Reject with the error message if bundling fails
112122
}
113123

124+
// Check if there are any errors in the bundling stats
114125
if (error || stats?.hasErrors()) {
115-
reject(stats.toString("errors-only"));
126+
reject(stats.toString("errors-only")); // Reject with errors-only details from stats
116127
}
117-
resolve(0);
128+
resolve(0); // Resolve successfully when bundling is complete
118129
});
119130
});
120131
}
121132

122133
module.exports = {
123-
web_bundling
134+
web_bundling // Export the web_bundling function to call it via metacall
124135
};

crates/metassr-bundler/src/lib.rs

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,27 @@ impl Default for CompilationWait {
3636
}
3737
}
3838

39+
/// A web bundler that invokes the `web_bundling` function from the Node.js `bundle.js` script
40+
/// using MetaCall. It is designed to bundle web resources like JavaScript and TypeScript files
41+
/// by calling a custom `rspack` configuration.
42+
///
43+
/// The `exec` function blocks the execution until the bundling process completes.
3944
#[derive(Debug)]
40-
4145
pub struct WebBundler<'a> {
46+
/// A map containing the source entry points for bundling.
47+
/// The key represents the entry name, and the value is the file path.
4248
pub targets: HashMap<String, &'a Path>,
49+
/// The output directory where the bundled files will be stored.
4350
pub dist_path: &'a Path,
4451
}
4552

4653
impl<'a> WebBundler<'a> {
54+
/// Creates a new `WebBundler` instance.
55+
///
56+
/// - `targets`: A HashMap where the key is a string representing an entry point, and the value is the file path.
57+
/// - `dist_path`: The path to the directory where the bundled output should be saved.
58+
///
59+
/// Returns a `WebBundler` struct.
4760
pub fn new<S>(targets: &'a HashMap<String, String>, dist_path: &'a S) -> Self
4861
where
4962
S: AsRef<OsStr> + ?Sized,
@@ -58,57 +71,75 @@ impl<'a> WebBundler<'a> {
5871
dist_path: Path::new(dist_path),
5972
}
6073
}
74+
75+
/// Executes the bundling process by invoking the `web_bundling` function from `bundle.js` via MetaCall.
76+
///
77+
/// It checks if the bundling script has been loaded, then calls the function and waits for the
78+
/// bundling to complete, either resolving successfully or logging an error.
79+
///
80+
/// # Errors
81+
///
82+
/// This function returns an `Err` if the bundling script cannot be loaded or if bundling fails.
6183
pub fn exec(&self) -> Result<()> {
84+
// Lock the mutex to check if the bundling script is already loaded
6285
let mut guard = IS_BUNDLING_SCRIPT_LOADED.lock().unwrap();
6386
if !guard.is_true() {
87+
// If not loaded, attempt to load the script into MetaCall
6488
if let Err(e) = loaders::from_memory("node", BUILD_SCRIPT) {
6589
return Err(anyhow!("Cannot load bundling script: {e:?}"));
6690
}
91+
// Mark the script as loaded
6792
guard.make_true();
6893
}
94+
// Drop the lock on the mutex as it's no longer needed
6995
drop(guard);
7096

97+
// Resolve callback when the bundling process is completed successfully
7198
fn resolve(_: Box<dyn MetacallValue>, _: Box<dyn MetacallValue>) {
7299
let compilation_wait = &*Arc::clone(&IS_COMPLIATION_WAIT);
73100
let mut started = compilation_wait.checker.lock().unwrap();
74101

102+
// Mark the process as completed and notify waiting threads
75103
started.make_true();
76-
// We notify the condvar that the value has changed
77104
compilation_wait.cond.notify_one();
78105
}
79106

107+
// Reject callback for handling errors during the bundling process
80108
fn reject(err: Box<dyn MetacallValue>, _: Box<dyn MetacallValue>) {
81109
let compilation_wait = &*Arc::clone(&IS_COMPLIATION_WAIT);
82110
let mut started = compilation_wait.checker.lock().unwrap();
83111

112+
// Log the bundling error and mark the process as completed
84113
error!("Bundling rejected: {err:?}");
85-
86114
started.make_true();
87-
// We notify the condvar that the value has changed
88115
compilation_wait.cond.notify_one();
89116
}
90117

118+
// Call the `web_bundling` function in the MetaCall script with targets and output path
91119
let future = metacall::<MetacallFuture>(
92120
BUNDLING_FUNC,
93121
[
122+
// Serialize the targets map to a string format
94123
serde_json::to_string(&self.targets)?,
124+
// Get the distribution path as a string
95125
self.dist_path.to_str().unwrap().to_owned(),
96126
],
97127
)
98128
.unwrap();
99129

130+
// Set the resolve and reject handlers for the bundling future
100131
future.then(resolve).catch(reject).await_fut();
101132

102-
// Wait for the thread to start up.
133+
// Lock the mutex and wait for the bundling process to complete
103134
let compilation_wait = Arc::clone(&IS_COMPLIATION_WAIT);
104-
105135
let mut started = compilation_wait.checker.lock().unwrap();
106136

107-
// Waiting till future done
137+
// Block the current thread until the bundling process signals completion
108138
while !started.is_true() {
109139
started = Arc::clone(&IS_COMPLIATION_WAIT).cond.wait(started).unwrap();
110140
}
111-
// Reset checker
141+
142+
// Reset the checker state to false after the process completes
112143
started.make_false();
113144
Ok(())
114145
}

0 commit comments

Comments
 (0)