Skip to content

Commit

Permalink
feat: simple custom worker syntax (#6899)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahabhgk authored Jun 24, 2024
1 parent cbbf227 commit 9284d3f
Show file tree
Hide file tree
Showing 18 changed files with 315 additions and 37 deletions.
1 change: 1 addition & 0 deletions crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,7 @@ export interface RawJavascriptParserOptions {
importExportsPresence?: string
reexportExportsPresence?: string
strictExportPresence: boolean
worker: Array<string>
}

export interface RawLazyCompilationOption {
Expand Down
2 changes: 2 additions & 0 deletions crates/rspack_binding_options/src/options/raw_module/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ pub struct RawJavascriptParserOptions {
pub import_exports_presence: Option<String>,
pub reexport_exports_presence: Option<String>,
pub strict_export_presence: bool,
pub worker: Vec<String>,
}

impl From<RawJavascriptParserOptions> for JavascriptParserOptions {
Expand All @@ -301,6 +302,7 @@ impl From<RawJavascriptParserOptions> for JavascriptParserOptions {
.reexport_exports_presence
.map(|e| ExportPresenceMode::from(e.as_str())),
strict_export_presence: value.strict_export_presence,
worker: value.worker,
}
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/rspack_core/src/options/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ pub struct JavascriptParserOptions {
pub import_exports_presence: Option<ExportPresenceMode>,
pub reexport_exports_presence: Option<ExportPresenceMode>,
pub strict_export_presence: bool,
pub worker: Vec<String>,
}

#[derive(Debug, Clone, MergeFrom)]
Expand Down
128 changes: 101 additions & 27 deletions crates/rspack_plugin_javascript/src/parser_plugin/worker_plugin.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::hash::Hash;

use itertools::Itertools;
use once_cell::sync::Lazy;
use regex::Regex;
use rspack_core::{
AsyncDependenciesBlock, ConstDependency, DependencyLocation, EntryOptions, ErrorSpan,
GroupOptions, SpanExt,
Expand All @@ -9,7 +11,7 @@ use rspack_hash::RspackHash;
use rustc_hash::FxHashSet;
use swc_core::{
common::{Span, Spanned},
ecma::ast::{Expr, ExprOrSpread, NewExpr},
ecma::ast::{CallExpr, Expr, ExprOrSpread, NewExpr},
};

use super::{
Expand Down Expand Up @@ -70,7 +72,7 @@ fn parse_new_worker_options_from_comments(

fn add_dependencies(
parser: &mut JavascriptParser,
new_expr: &NewExpr,
span: Span,
parsed_path: ParsedNewWorkerPath,
parsed_options: Option<ParsedNewWorkerOptions>,
) {
Expand All @@ -86,7 +88,7 @@ fn add_dependencies(
let range = parsed_options.as_ref().and_then(|options| options.range);
let name = parsed_options.and_then(|options| options.name);
let output_module = output_options.module;
let span = ErrorSpan::from(new_expr.span);
let span = ErrorSpan::from(span);
let dep = Box::new(WorkerDependency::new(
parsed_path.range.0,
parsed_path.range.1,
Expand Down Expand Up @@ -146,12 +148,12 @@ fn add_dependencies(
}
}

fn parse_new_worker(
fn handle_worker(
parser: &mut JavascriptParser,
new_expr: &NewExpr,
args: &[ExprOrSpread],
span: Span,
) -> Option<(ParsedNewWorkerPath, Option<ParsedNewWorkerOptions>)> {
if let Some(args) = &new_expr.args
&& let Some(expr_or_spread) = args.first()
if let Some(expr_or_spread) = args.first()
&& let ExprOrSpread {
spread: None,
expr: box Expr::New(new_url_expr),
Expand All @@ -178,7 +180,7 @@ fn parse_new_worker(
})
.or_else(|| {
// new Worker(/* options */ new URL("worker.js"))
parse_new_worker_options_from_comments(parser, expr_or_spread.span(), new_expr.span())
parse_new_worker_options_from_comments(parser, expr_or_spread.span(), span)
});
Some((path, options))
} else {
Expand All @@ -188,27 +190,91 @@ fn parse_new_worker(

pub struct WorkerPlugin {
new_syntax: FxHashSet<String>,
// call_syntax: FxHashSet<String>,
call_syntax: FxHashSet<String>,
from_new_syntax: FxHashSet<(String, String)>,
// from_call_syntax: FxHashSet<(String, String)>,
from_call_syntax: FxHashSet<(String, String)>,
}

static WORKER_FROM_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^(.+?)(\(\))?\s+from\s+(.+)$").expect("invalid regex"));

impl WorkerPlugin {
pub fn new(/* syntax_list: &[&str] */) -> Self {
Self {
new_syntax: FxHashSet::from_iter(["Worker".into(), "SharedWorker".into()]),
// call_syntax: FxHashSet::default(),
from_new_syntax: FxHashSet::from_iter([("Worker".into(), "worker_threads".into())]),
// from_call_syntax: FxHashSet::default(),
pub fn new(syntax_list: &[String]) -> Self {
let mut this = Self {
new_syntax: FxHashSet::default(),
call_syntax: FxHashSet::default(),
from_new_syntax: FxHashSet::default(),
from_call_syntax: FxHashSet::default(),
};
for syntax in syntax_list {
if let Some(syntax) = syntax.strip_suffix("()") {
this.call_syntax.insert(syntax.to_string());
} else if let Some(captures) = WORKER_FROM_REGEX.captures(syntax) {
let ids = &captures[1];
let is_call = &captures.get(2).is_some();
let source = &captures[3];
if *is_call {
this
.from_call_syntax
.insert((ids.to_string(), source.to_string()));
} else {
this
.from_new_syntax
.insert((ids.to_string(), source.to_string()));
}
} else {
this.new_syntax.insert(syntax.to_string());
}
}
this
}
}

impl JavascriptParserPlugin for WorkerPlugin {
fn call(
&self,
parser: &mut JavascriptParser,
call_expr: &CallExpr,
for_name: &str,
) -> Option<bool> {
if for_name == HARMONY_SPECIFIER_TAG {
let tag_info = parser
.definitions_db
.expect_get_tag_info(&parser.current_tag_info?);
let settings = HarmonySpecifierData::downcast(tag_info.data.clone()?);
let ids = settings.ids.iter().map(|id| id.as_str()).join(".");
if self
.from_call_syntax
.contains(&(ids, settings.source.to_string()))
{
return handle_worker(parser, &call_expr.args, call_expr.span).map(
|(parsed_path, parsed_options)| {
add_dependencies(parser, call_expr.span, parsed_path, parsed_options);
if let Some(callee) = call_expr.callee.as_expr() {
parser.walk_expression(callee);
}
true
},
);
}
return None;
}
if !self.call_syntax.contains(for_name) {
return None;
}
handle_worker(parser, &call_expr.args, call_expr.span).map(|(parsed_path, parsed_options)| {
add_dependencies(parser, call_expr.span, parsed_path, parsed_options);
if let Some(callee) = call_expr.callee.as_expr() {
parser.walk_expression(callee);
}
true
})
}

fn new_expression(
&self,
parser: &mut JavascriptParser,
new_expr: &swc_core::ecma::ast::NewExpr,
new_expr: &NewExpr,
for_name: &str,
) -> Option<bool> {
if for_name == HARMONY_SPECIFIER_TAG {
Expand All @@ -221,21 +287,29 @@ impl JavascriptParserPlugin for WorkerPlugin {
.from_new_syntax
.contains(&(ids, settings.source.to_string()))
{
return parse_new_worker(parser, new_expr).map(|(parsed_path, parsed_options)| {
add_dependencies(parser, new_expr, parsed_path, parsed_options);
parser.walk_expression(&new_expr.callee);
true
});
return new_expr
.args
.as_ref()
.and_then(|args| handle_worker(parser, args, new_expr.span))
.map(|(parsed_path, parsed_options)| {
add_dependencies(parser, new_expr.span, parsed_path, parsed_options);
parser.walk_expression(&new_expr.callee);
true
});
}
return None;
}
if !self.new_syntax.contains(for_name) {
return None;
}
parse_new_worker(parser, new_expr).map(|(parsed_path, parsed_options)| {
add_dependencies(parser, new_expr, parsed_path, parsed_options);
parser.walk_expression(&new_expr.callee);
true
})
new_expr
.args
.as_ref()
.and_then(|args| handle_worker(parser, args, new_expr.span))
.map(|(parsed_path, parsed_options)| {
add_dependencies(parser, new_expr.span, parsed_path, parsed_options);
parser.walk_expression(&new_expr.callee);
true
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,9 @@ impl<'parser> JavascriptParser<'parser> {
relative: matches!(parse_url, JavascriptParserUrl::Relative),
}));
}
plugins.push(Box::new(parser_plugin::WorkerPlugin::new()));
plugins.push(Box::new(parser_plugin::WorkerPlugin::new(
&javascript_options.worker,
)));
}

let plugin_drive = Rc::new(JavaScriptParserPluginDrive::new(plugins));
Expand Down
7 changes: 7 additions & 0 deletions crates/rspack_util/src/merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ impl<T: MergeFrom> MergeFrom for Option<T> {
}
}

impl<T: MergeFrom> MergeFrom for Vec<T> {
fn merge_from(mut self, other: &Self) -> Self {
self.extend(other.iter().cloned());
self
}
}

impl_merge_from!(i8, i16, i32, i64, i128);
impl_merge_from!(u8, u16, u32, u64, u128);
impl_merge_from!(bool);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ Object {
"reexportExportsPresence": undefined,
"strictExportPresence": false,
"url": true,
"worker": Array [
"...",
],
"wrappedContextCritical": false,
},
"javascript/auto": Object {
Expand All @@ -218,6 +221,9 @@ Object {
"reexportExportsPresence": undefined,
"strictExportPresence": false,
"url": true,
"worker": Array [
"...",
],
"wrappedContextCritical": false,
},
"javascript/dynamic": Object {
Expand All @@ -230,6 +236,9 @@ Object {
"reexportExportsPresence": undefined,
"strictExportPresence": false,
"url": true,
"worker": Array [
"...",
],
"wrappedContextCritical": false,
},
"javascript/esm": Object {
Expand All @@ -242,6 +251,9 @@ Object {
"reexportExportsPresence": undefined,
"strictExportPresence": false,
"url": true,
"worker": Array [
"...",
],
"wrappedContextCritical": false,
},
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
it("should allow to create a WebWorker", async () => {
const worker = MyWorker(new URL("./worker.js", import.meta.url), {
type: "module"
});
worker.postMessage("ok");
const result = await new Promise(resolve => {
worker.onmessage = event => {
resolve(event.data);
};
});
expect(result).toBe("data: OK, thanks");
await worker.terminate();
});

it("should allow to share chunks", async () => {
const promise = import("./module");
const script = document.head._children[0];
const src = script.src;
const file = src.slice(src.lastIndexOf("/"));
__non_webpack_require__(`./${file}`);
script.onload();
const { upper } = await promise;
expect(upper("ok")).toBe("OK");
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function upper(str) {
return str.toUpperCase();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
findBundle: function(i, options) {
return ["main.js"];
},
moduleScope(scope) {
scope["MyWorker"] = (...args) => new scope.Worker(...args);
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/** @type {import("@rspack/core").Configuration} */
module.exports = {
output: {
filename: "[name].js"
},
target: "web",
module: {
rules: [
{
test: /\.[cm]?js$/,
parser: {
worker: ["MyWorker()", "..."]
}
}
]
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
onmessage = async event => {
const { upper } = await import("./module");
postMessage(`data: ${upper(event.data)}, thanks`);
};
Loading

2 comments on commit 9284d3f

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Ran ecosystem CI: Open

suite result
modernjs ❌ failure
_selftest ✅ success
nx ✅ success
rspress ❌ failure
rsbuild ❌ failure
compat ✅ success
examples ❌ failure

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Benchmark detail: Open

Name Base (2024-06-20 5839629) Current Change
10000_development-mode + exec 2.21 s ± 12 ms 2.19 s ± 9.1 ms -1.09 %
10000_development-mode_hmr + exec 714 ms ± 9.7 ms 704 ms ± 17 ms -1.43 %
10000_production-mode + exec 2.77 s ± 28 ms 2.68 s ± 35 ms -3.04 %
arco-pro_development-mode + exec 1.9 s ± 78 ms 1.91 s ± 73 ms +0.62 %
arco-pro_development-mode_hmr + exec 439 ms ± 0.92 ms 435 ms ± 4 ms -0.87 %
arco-pro_production-mode + exec 3.47 s ± 86 ms 3.44 s ± 62 ms -0.79 %
threejs_development-mode_10x + exec 2.06 s ± 21 ms 2.05 s ± 20 ms -0.48 %
threejs_development-mode_10x_hmr + exec 812 ms ± 4.1 ms 813 ms ± 11 ms +0.10 %
threejs_production-mode_10x + exec 5.33 s ± 24 ms 5.27 s ± 19 ms -1.01 %

Please sign in to comment.