Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: simple custom worker syntax #6899

Merged
merged 2 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,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
Loading