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: custom worker syntax for worklet #6916

Merged
merged 3 commits into from
Jun 25, 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
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,6 @@ struct NestedRequireData {
loc: DependencyLocation,
}

impl TagInfoData for NestedRequireData {
fn into_any(data: Self) -> Box<dyn anymap::CloneAny + 'static> {
Box::new(data)
}

fn downcast(any: Box<dyn anymap::CloneAny>) -> Self {
*(any as Box<dyn std::any::Any>)
.downcast()
.expect("NestedRequireData should be downcasted from correct tag info")
}
}

pub struct CompatibilityPlugin;

impl CompatibilityPlugin {
Expand Down
32 changes: 32 additions & 0 deletions crates/rspack_plugin_javascript/src/parser_plugin/drive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,38 @@ impl JavascriptParserPlugin for JavaScriptParserPluginDrive {
None
}

fn pattern(
&self,
parser: &mut JavascriptParser,
ident: &swc_core::ecma::ast::Ident,
for_name: &str,
) -> Option<bool> {
for plugin in &self.plugins {
let res = plugin.pattern(parser, ident, for_name);
// `SyncBailHook`
if res.is_some() {
return res;
}
}
None
}

fn pre_declarator(
&self,
parser: &mut JavascriptParser,
declarator: &VarDeclarator,
declaration: &VarDecl,
) -> Option<bool> {
for plugin in &self.plugins {
let res = plugin.pre_declarator(parser, declarator, declaration);
// `SyncBailHook`
if res.is_some() {
return res;
}
}
None
}

fn can_rename(&self, parser: &mut JavascriptParser, str: &str) -> Option<bool> {
for plugin in &self.plugins {
let res = plugin.can_rename(parser, str);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,18 +81,6 @@ pub struct HarmonySpecifierData {
pub source_order: i32,
}

impl TagInfoData for HarmonySpecifierData {
fn into_any(data: Self) -> Box<dyn anymap::CloneAny> {
Box::new(data)
}

fn downcast(any: Box<dyn anymap::CloneAny>) -> Self {
*(any as Box<dyn std::any::Any>)
.downcast()
.expect("HarmonySpecifierData should be downcasted from correct tag info")
}
}

impl JavascriptParserPlugin for HarmonyImportDependencyParserPlugin {
fn import(
&self,
Expand Down
18 changes: 18 additions & 0 deletions crates/rspack_plugin_javascript/src/parser_plugin/trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ pub trait JavascriptParserPlugin {
None
}

fn pre_declarator(
&self,
_parser: &mut JavascriptParser,
_declarator: &VarDeclarator,
_declaration: &VarDecl,
) -> Option<bool> {
None
}

fn evaluate_typeof(
&self,
_parser: &mut JavascriptParser,
Expand Down Expand Up @@ -81,6 +90,15 @@ pub trait JavascriptParserPlugin {
None
}

fn pattern(
&self,
_parser: &mut JavascriptParser,
_ident: &Ident,
_for_name: &str,
) -> Option<bool> {
None
}

fn call(
&self,
_parser: &mut JavascriptParser,
Expand Down
97 changes: 94 additions & 3 deletions crates/rspack_plugin_javascript/src/parser_plugin/worker_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ use rspack_core::{
GroupOptions, SpanExt,
};
use rspack_hash::RspackHash;
use rustc_hash::FxHashSet;
use rustc_hash::{FxHashMap, FxHashSet};
use swc_core::{
atoms::Atom,
common::{Span, Spanned},
ecma::ast::{CallExpr, Expr, ExprOrSpread, NewExpr},
ecma::ast::{CallExpr, Expr, ExprOrSpread, Ident, NewExpr, VarDecl, VarDeclarator},
};

use super::{
Expand Down Expand Up @@ -193,21 +194,44 @@ pub struct WorkerPlugin {
call_syntax: FxHashSet<String>,
from_new_syntax: FxHashSet<(String, String)>,
from_call_syntax: FxHashSet<(String, String)>,
pattern_syntax: FxHashMap<String, FxHashSet<String>>,
}

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

const WORKER_SPECIFIER_TAG: &str = "_identifier__worker_specifier_tag__";

#[derive(Debug, Clone)]
struct WorkerSpecifierData {
key: Atom,
}

impl WorkerPlugin {
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(),
pattern_syntax: FxHashMap::default(),
};
for syntax in syntax_list {
if let Some(syntax) = syntax.strip_suffix("()") {
if let Some(syntax) = syntax.strip_prefix('*')
&& let Some(first_dot) = syntax.find('.')
&& let Some(syntax) = syntax.strip_suffix("()")
{
let pattern = &syntax[0..first_dot];
let members = &syntax[first_dot + 1..];
if let Some(value) = this.pattern_syntax.get_mut(pattern) {
value.insert(members.to_string());
} else {
this.pattern_syntax.insert(
pattern.to_string(),
FxHashSet::from_iter([members.to_string()]),
);
}
} else 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];
Expand All @@ -231,6 +255,73 @@ impl WorkerPlugin {
}

impl JavascriptParserPlugin for WorkerPlugin {
fn pre_declarator(
&self,
parser: &mut JavascriptParser,
decl: &VarDeclarator,
_statement: &VarDecl,
) -> Option<bool> {
if let Some(ident) = decl.name.as_ident()
&& self.pattern_syntax.contains_key(ident.sym.as_str())
{
parser.tag_variable(
ident.sym.to_string(),
WORKER_SPECIFIER_TAG,
Some(WorkerSpecifierData {
key: ident.sym.clone(),
}),
);
return Some(true);
}
None
}

fn pattern(&self, parser: &mut JavascriptParser, ident: &Ident, for_name: &str) -> Option<bool> {
if self.pattern_syntax.contains_key(for_name) {
parser.tag_variable(
ident.sym.to_string(),
WORKER_SPECIFIER_TAG,
Some(WorkerSpecifierData {
key: ident.sym.clone(),
}),
);
return Some(true);
}
None
}

fn call_member_chain(
&self,
parser: &mut JavascriptParser,
call_expr: &CallExpr,
for_name: &str,
members: &[Atom],
_members_optionals: &[bool],
_member_ranges: &[Span],
) -> Option<bool> {
if for_name != WORKER_SPECIFIER_TAG {
return None;
}
let tag_info = parser
.definitions_db
.expect_get_tag_info(&parser.current_tag_info?);
let data = WorkerSpecifierData::downcast(tag_info.data.clone()?);
if let Some(value) = self.pattern_syntax.get(data.key.as_str())
&& value.contains(&members.iter().map(|id| id.as_str()).join("."))
{
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
},
);
}
None
}

fn call(
&self,
parser: &mut JavascriptParser,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,27 @@ use crate::visitors::scope_info::{
FreeName, ScopeInfoDB, ScopeInfoId, TagInfo, TagInfoId, VariableInfo, VariableInfoId,
};

pub trait TagInfoData {
pub trait TagInfoData: Clone + Sized + 'static {
fn into_any(data: Self) -> Box<dyn anymap::CloneAny>;

fn downcast(any: Box<dyn anymap::CloneAny>) -> Self;
}

impl<T> TagInfoData for T
where
T: Clone + Sized + 'static,
{
fn into_any(data: Self) -> Box<dyn anymap::CloneAny> {
Box::new(data)
}

fn downcast(any: Box<dyn anymap::CloneAny>) -> Self {
*(any as Box<dyn std::any::Any>)
.downcast()
.expect("TagInfoData should be downcasted from correct tag info")
}
}

#[derive(Debug)]
pub struct ExtractedMemberExpressionChainData {
pub object: Expr,
Expand Down Expand Up @@ -699,8 +715,15 @@ impl<'parser> JavascriptParser<'parser> {
where
F: FnOnce(&mut Self, &Ident),
{
// TODO: add hooks here;
on_ident(self, ident);
if !ident
.sym
.call_hooks_name(self, |parser, for_name| {
parser.plugin_drive.clone().pattern(parser, ident, for_name)
})
.unwrap_or_default()
{
on_ident(self, ident);
}
}

fn enter_array_pattern<F>(&mut self, array_pat: &ArrayPat, on_ident: F)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,16 @@ impl<'parser> JavascriptParser<'parser> {
pub(super) fn _pre_walk_variable_declaration(&mut self, decl: &VarDecl) {
for declarator in &decl.decls {
self.pre_walk_variable_declarator(declarator);
// TODO: hooks.pre_declarator
self.enter_pattern(Cow::Borrowed(&declarator.name), |this, ident| {
this.define_variable(ident.sym.to_string());
});
if !self
.plugin_drive
.clone()
.pre_declarator(self, declarator, decl)
.unwrap_or_default()
{
self.enter_pattern(Cow::Borrowed(&declarator.name), |this, ident| {
this.define_variable(ident.sym.to_string());
});
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/rspack/src/config/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ function getRawJavascriptParserOptionsWorker(
const DEFAULT_SYNTAX = [
"Worker",
"SharedWorker",
// "navigator.serviceWorker.register()",
"navigator.serviceWorker.register()",
"Worker from worker_threads"
];
return (
Expand Down
24 changes: 24 additions & 0 deletions webpack-test/configCases/worker/issue-17489/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
let audioContext = null;

it("should allow to create a paintWorklet worklet", async () => {
if (audioContext === null) {
audioContext = new AudioContext();
}

let pseudoWorklet = await audioContext.audioWorklet.addModule(new URL("./worklet.js", import.meta.url));

pseudoWorklet = new pseudoWorklet();

expect(pseudoWorklet.url).not.toContain("asset-");

pseudoWorklet.postMessage("ok");

const result = await new Promise(resolve => {
pseudoWorklet.onmessage = event => {
resolve(event.data);
};
});
expect(result).toBe("data: OK, thanks");

await pseudoWorklet.terminate();
})
3 changes: 3 additions & 0 deletions webpack-test/configCases/worker/issue-17489/module.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function upper(str) {
return str.toUpperCase();
}
22 changes: 22 additions & 0 deletions webpack-test/configCases/worker/issue-17489/test.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
let outputDirectory;

module.exports = {
moduleScope(scope) {
const FakeWorker = require("../../../helpers/createFakeWorker")({
outputDirectory
});

// Pseudo code
scope.AudioContext = class AudioContext {
constructor() {
this.audioWorklet = {
addModule: url => Promise.resolve(FakeWorker.bind(null, url))
};
}
};
},
findBundle: function (i, options) {
outputDirectory = options.output.path;
return ["main.js"];
}
};
5 changes: 5 additions & 0 deletions webpack-test/configCases/worker/issue-17489/test.filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
var supportsWorker = require("../../../helpers/supportsWorker");

module.exports = function (config) {
return supportsWorker();
};
20 changes: 20 additions & 0 deletions webpack-test/configCases/worker/issue-17489/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/** @type {import("../../../../").Configuration} */
module.exports = {
output: {
filename: "[name].js"
},
optimization: {
innerGraph: true
},
target: "web",
module: {
rules: [
{
test: /\.[cm]?js$/,
parser: {
worker: ["*audioContext.audioWorklet.addModule()", "..."]
}
}
]
}
};
Loading
Loading